├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── animation.gif ├── app.rb ├── app ├── helpers │ ├── async.rb │ ├── json.rb │ └── sse.rb └── public │ ├── css │ ├── bootstrap-theme.css │ ├── bootstrap.css │ ├── codemirror-solarized.css │ ├── codemirror.css │ ├── prism.css │ └── style.css │ ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff │ ├── html │ ├── 404.html │ ├── execute.html │ ├── index.html │ ├── keyspace.html │ └── table.html │ ├── js │ ├── angular-filter.min.js │ ├── cassandra.js │ ├── codemirror-sql.js │ ├── codemirror.js │ ├── prism.js │ ├── prism.min.js │ ├── ui-bootstrap-tpls.min.js │ └── ui-codemirror.js │ └── main.html ├── bin └── cassandra-web └── cassandra-web.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ project info 2 | .idea 3 | *.iml 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # A sample Gemfile 2 | source "https://rubygems.org" 3 | 4 | gemspec 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | cassandra-web (0.5.0) 5 | cassandra-driver (~> 3.1) 6 | lz4-ruby (~> 0.3) 7 | rack-cors (>= 0.2, < 2.0) 8 | rack-parser (~> 0.6) 9 | sinatra (~> 1.4) 10 | thin (~> 1.6) 11 | 12 | GEM 13 | remote: https://rubygems.org/ 14 | specs: 15 | cassandra-driver (3.2.0) 16 | ione (~> 1.2) 17 | daemons (1.3.1) 18 | eventmachine (1.2.7) 19 | ione (1.2.4) 20 | lz4-ruby (0.3.3) 21 | rack (1.6.13) 22 | rack-cors (1.0.6) 23 | rack (>= 1.6.0) 24 | rack-parser (0.7.0) 25 | rack 26 | rack-protection (1.5.5) 27 | rack 28 | rake (12.3.3) 29 | sinatra (1.4.8) 30 | rack (~> 1.5) 31 | rack-protection (~> 1.4) 32 | tilt (>= 1.3, < 3) 33 | thin (1.7.2) 34 | daemons (~> 1.0, >= 1.0.9) 35 | eventmachine (~> 1.0, >= 1.0.4) 36 | rack (>= 1, < 3) 37 | tilt (2.0.10) 38 | 39 | PLATFORMS 40 | ruby 41 | 42 | DEPENDENCIES 43 | bundler (~> 1.6) 44 | cassandra-web! 45 | rake (~> 12.3) 46 | 47 | BUNDLED WITH 48 | 1.17.3 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 avalanche123 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cassandra Web 2 | 3 | A web interface to Apache Cassandra with AngularJS and server-sent events. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | gem install cassandra-web 9 | ``` 10 | 11 | ## Usage 12 | 13 | Run `cassandra-web -h` for help. 14 | 15 | ### Quick Start 16 | 17 | ```bash 18 | cassandra-web 19 | ``` 20 | 21 | ### Connect to a Cassandra Cluster requiring authentication 22 | 23 | ```bash 24 | cassandra-web --hosts '10.0.2.2' --port '9042' --username 'cassweb' --password 'myPassword' 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ## How it works 32 | 33 | Cassandra web consists of an HTTP API powered by [Sinatra](https://github.com/sinatra/sinatra) and a thin HTML5/JavaScript frontend powered by [AngularJS](https://angularjs.org/). 34 | 35 | When you run `cassandra-web` script, it starts a [Thin web server](http://code.macournoyer.com/thin/) on a specified address, which defaults to `localhost:3000`. Openning `http://localhost:3000`, or whatever address you've specified in the browser, loads the AngularJS application and it starts interacting with the HTTP API of `cassandra-web`. This api uses the [Ruby Driver](http://datastax.github.io/ruby-driver/) to communicate with an [Apache Cassandra](http://cassandra.apache.org/) cluster. 36 | 37 | When the frontend has fully loaded, [it subscribes to `/events` API endpoint](https://github.com/avalanche123/cassandra-web/blob/master/app/public/js/cassandra.js#L108), and begins receiving [Server Sent Events](http://www.w3.org/TR/2012/WD-eventsource-20120426/). [The API uses an event listener, which is registered with the `Cluster` instance created by the Ruby Driver, to stream events](https://github.com/avalanche123/cassandra-web/blob/master/app/helpers/sse.rb#L43-L56) such as [schema](https://github.com/avalanche123/cassandra-web/blob/master/app/helpers/sse.rb#L29-L39) and [node status](https://github.com/avalanche123/cassandra-web/blob/master/app/helpers/sse.rb#L13-L27) changes to update the user interface without having to refresh the page. 38 | 39 | You can see this feature in action by creating a keyspace using the execute button in the top-right corner of the UI and executing the following statement: 40 | 41 | ```cql 42 | CREATE KEYSPACE example WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1} 43 | ``` 44 | 45 | If the statement executed successfully, you should see a new keyspace show up on the left side of the UI. 46 | 47 | ![Alt text](https://raw.githubusercontent.com/avalanche123/cassandra-web/master/animation.gif "Create Keyspace") 48 | 49 | The web server, Thin, used by `cassandra-web` is asynchronous and uses only a single thread to handle requests. This enables efficient handling multiple of long running connections, which is a requirement for streaming and Server Sent Events, but also means that the application cannot perform blocking operations during request handling, since it would hang up all connections for the duration of the blocking operation. `cassandra-web` therefore uses Asynchronous Execution feature of the Ruby Driver to not block on statements execution. [The application executes statements asynchronously, receiving a future from the Ruby Driver](https://github.com/avalanche123/cassandra-web/blob/master/app.rb#L88). [It then registers future completion listeners to send a response (or error) whenever it becomes available](https://github.com/avalanche123/cassandra-web/blob/master/app/helpers/async.rb#L7-L40). 50 | 51 | ## Credits 52 | 53 | Cassandra web is possible because of the following awesome technologies (in no particular order): 54 | 55 | * [Apache Cassandra](http://cassandra.apache.org/) 56 | * [DataStax Ruby Driver for Apache Cassandra](http://datastax.github.io/ruby-driver/) 57 | * [Sinatra](https://github.com/sinatra/sinatra) 58 | * [AngularJS](https://angularjs.org/) 59 | * [Twitter Bootstrap](http://getbootstrap.com/) 60 | * [Thin](http://code.macournoyer.com/thin/) 61 | * [Server Sent Events](http://www.w3.org/TR/2012/WD-eventsource-20120426/) 62 | * [PrismJS](http://prismjs.com/) 63 | * [CodeMirror](http://codemirror.net/) 64 | * and many others... 65 | -------------------------------------------------------------------------------- /animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avalanche123/cassandra-web/f11e47a26f316827f631d7bcfec14b9dd94f44be/animation.gif -------------------------------------------------------------------------------- /app.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'logger' 4 | 5 | require 'rubygems' 6 | require 'bundler/setup' 7 | 8 | require 'sinatra/base' 9 | require 'rack/cors' 10 | require 'rack/parser' 11 | require 'cassandra' 12 | require 'json' 13 | 14 | $: << File.expand_path('../', __FILE__) 15 | 16 | class App < Sinatra::Base 17 | configure do 18 | set :root, File.expand_path('../', __FILE__) + '/app' 19 | 20 | enable :static 21 | disable :views 22 | disable :method_override 23 | enable :protection 24 | end 25 | 26 | use Rack::Cors do 27 | allow do 28 | origins /.*/ 29 | resource '/*', 30 | :methods => [:get, :post, :put, :delete, :options], 31 | :expose => ['Location'], 32 | :headers => :any 33 | end 34 | end 35 | 36 | use Rack::Parser, content_types: { 37 | 'application/json' => JSON.method(:load) 38 | } 39 | 40 | get '/events/?' do 41 | status 200 42 | content_type 'text/event-stream' 43 | headers 'Connection' => 'keep-alive' 44 | 45 | stream(:keep_open) do |out| 46 | stream_events(out) 47 | end 48 | end 49 | 50 | get '/hosts/?' do 51 | status 200 52 | content_type 'application/json' 53 | 54 | json_dump(cluster.hosts) 55 | end 56 | 57 | get '/keyspaces/?' do 58 | status 200 59 | content_type 'application/json' 60 | 61 | json_dump(cluster.keyspaces) 62 | end 63 | 64 | get '/consistencies/' do 65 | status 200 66 | content_type 'application/json' 67 | 68 | json_dump(Cassandra::CONSISTENCIES) 69 | end 70 | 71 | post '/execute/?' do 72 | content_type 'application/json' 73 | 74 | statement = params['statement'] 75 | statement.strip! 76 | statement.chomp!(";") 77 | 78 | options = { 79 | :consistency => :one, 80 | :trace => false 81 | } 82 | 83 | if params['options'] 84 | options[:trace] = !!params['options']['trace'] if params['options'].has_key?('trace') 85 | options[:consistency] = params['options']['consistency'].to_sym if params['options'].has_key?('consistency') && Cassandra::CONSISTENCIES.include?(params['options']['consistency'].to_sym) 86 | end 87 | 88 | defer(session.execute_async(statement, options)) 89 | end 90 | 91 | get '*' do 92 | File.read(File.join(settings.public_folder, 'main.html')) 93 | end 94 | end 95 | 96 | require 'app/helpers/async' 97 | require 'app/helpers/json' 98 | require 'app/helpers/sse' 99 | 100 | App.helpers App::Helpers::Async 101 | App.helpers App::Helpers::JSON 102 | App.helpers App::Helpers::SSE 103 | -------------------------------------------------------------------------------- /app/helpers/async.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class App 4 | module Helpers 5 | module Async 6 | def defer(future) 7 | future.on_success do |result| 8 | env["async.callback"].call([ 9 | 200, 10 | { 11 | 'Content-Type' => 'application/json' 12 | }, 13 | [json_dump(result)] 14 | ]) 15 | end 16 | 17 | future.on_failure do |error| 18 | status = case error 19 | when Cassandra::Errors::NoHostsAvailable 20 | 503 21 | when Cassandra::Errors::AuthenticationError 22 | 401 23 | when Cassandra::Errors::UnauthorizedError 24 | 403 25 | when Cassandra::Errors::ExecutionError 26 | 504 27 | when Cassandra::Error 28 | 400 29 | when 30 | 500 31 | end 32 | 33 | env["async.callback"].call([ 34 | status, 35 | { 36 | 'Content-Type' => 'application/json' 37 | }, 38 | [json_dump(error)] 39 | ]) 40 | end 41 | 42 | throw :async 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /app/helpers/json.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class App 4 | module Helpers 5 | module JSON 6 | def json_dump(object) 7 | ::JSON.dump(object_hash(object)) 8 | end 9 | 10 | private 11 | 12 | def object_hash(object) 13 | case object 14 | when ::Cassandra::Host 15 | host_hash(object) 16 | when ::Cassandra::Keyspace 17 | keyspace_hash(object) 18 | when ::Cassandra::Result 19 | result_hash(object) 20 | when ::Exception 21 | exception_hash(object) 22 | when ::Hash 23 | hash = ::Hash.new 24 | object.each do |key, value| 25 | hash[key] = object_hash(value) 26 | end 27 | hash 28 | when ::Enumerable 29 | object.map(&method(:object_hash)) 30 | when ::String 31 | begin 32 | object.encode('utf-8') 33 | rescue Encoding::UndefinedConversionError 34 | '0x' + object.unpack('H*').first 35 | end 36 | when nil 37 | 'null' 38 | else 39 | object.to_s 40 | end 41 | end 42 | 43 | def object_key(object) 44 | case object 45 | when ::Cassandra::Host 46 | object.ip 47 | when ::Cassandra::Keyspace 48 | object.name 49 | else 50 | raise "unsupported object #{object.inspect}" 51 | end 52 | end 53 | 54 | def host_hash(host) 55 | { 56 | :ip => host.ip, 57 | :id => host.id, 58 | :datacenter => host.datacenter, 59 | :rack => host.rack, 60 | :release_version => host.release_version, 61 | :status => host.status 62 | } 63 | end 64 | 65 | def keyspace_hash(keyspace) 66 | { 67 | :name => keyspace.name, 68 | :cql => keyspace.to_cql, 69 | :tables => keyspace.tables.map do |table| 70 | { 71 | :name => table.name, 72 | :cql => table.to_cql, 73 | :columns => table.columns.map do |column| 74 | { 75 | :name => column.name, 76 | :type => column_type_hash(column.type), 77 | :order => column.order 78 | } 79 | end 80 | } 81 | end 82 | } 83 | end 84 | 85 | def column_type_hash(column_type) 86 | case column_type 87 | when Array 88 | column_type.first.to_s + '<' + column_type.slice(1..-1).join(', ') + '>' 89 | else 90 | column_type 91 | end 92 | end 93 | 94 | def result_hash(result) 95 | { 96 | :rows => result.map(&method(:object_hash)), 97 | :columns => columns_hash(result), 98 | :info => execution_info_hash(result.execution_info), 99 | } 100 | end 101 | 102 | def columns_hash(rows) 103 | return [] if rows.empty? 104 | rows.first.keys 105 | end 106 | 107 | def execution_info_hash(execution_info) 108 | { 109 | :keyspace => execution_info.keyspace, 110 | :statement => statement_hash(execution_info.statement), 111 | :options => execution_options_hash(execution_info.options), 112 | :hosts => execution_info.hosts.map(&method(:host_hash)), 113 | :consistency => execution_info.consistency, 114 | :retries => execution_info.retries, 115 | :trace => execution_info.trace && execution_trace_hash(execution_info.trace) 116 | } 117 | end 118 | 119 | def statement_hash(statement) 120 | { 121 | :cql => statement.cql 122 | } 123 | end 124 | 125 | def execution_options_hash(execution_options) 126 | { 127 | :consistency => execution_options.consistency, 128 | :serial_consistency => execution_options.serial_consistency, 129 | :page_size => execution_options.page_size, 130 | :timeout => execution_options.timeout, 131 | :trace => execution_options.trace? 132 | } 133 | end 134 | 135 | def execution_trace_hash(execution_trace) 136 | { 137 | :id => execution_trace.id, 138 | :coordinator => execution_trace.coordinator, 139 | :duration => execution_trace.duration, 140 | :parameters => execution_trace.parameters, 141 | :request => execution_trace.request, 142 | :started_at => execution_trace.started_at, 143 | :events => execution_trace.events.map(&method(:execution_trace_event_hash)) 144 | } 145 | end 146 | 147 | def execution_trace_event_hash(execution_trace_event) 148 | { 149 | :id => execution_trace_event.id, 150 | :activity => execution_trace_event.activity, 151 | :source => execution_trace_event.source, 152 | :source_elapsed => execution_trace_event.source_elapsed, 153 | :thread => execution_trace_event.thread 154 | } 155 | end 156 | 157 | def error_hash(error) 158 | case error 159 | when ::Cassandra::Errors::NoHostsAvailable 160 | no_hosts_available_error_hash(error) 161 | when ::Cassandra::Errors::ReadTimeoutError 162 | read_timeout_error_hash(error) 163 | when ::Cassandra::Errors::WriteTimeoutError 164 | write_timeout_error_hash(error) 165 | when ::Cassandra::Errors::UnavailableError 166 | unavailable_error_hash(error) 167 | when ::Cassandra::Errors::UnpreparedError 168 | unprepared_error_hash(error) 169 | when ::Cassandra::Errors::AlreadyExistsError 170 | already_exists_error_hash(error) 171 | when ::Cassandra::Errors::ExecutionError, ::Cassandra::Errors::ValidationError 172 | execution_error_hash(error) 173 | else 174 | exception_hash(error) 175 | end 176 | end 177 | 178 | def exception_hash(error) 179 | { 180 | :class => error.class.name, 181 | :message => error.message, 182 | :trace => error.backtrace 183 | } 184 | end 185 | 186 | def execution_error_hash(error) 187 | hash = exception_hash(error) 188 | hash[:statement] = statement_hash(error.statement) 189 | hash 190 | end 191 | 192 | def already_exists_error_hash(error) 193 | hash = execution_error_hash(error) 194 | hash[:keyspace] = error.keyspace 195 | hash[:table] = error.table 196 | hash 197 | end 198 | 199 | def unprepared_error_hash(error) 200 | hash = execution_error_hash(error) 201 | hash[:id] = error.id 202 | hash 203 | end 204 | 205 | def read_timeout_error_hash(error) 206 | hash = execution_error_hash(error) 207 | hash[:retrieved] = error.retrieved? 208 | hash[:consistency] = error.consistency 209 | hash[:required] = error.required 210 | hash[:received] = error.received 211 | hash 212 | end 213 | 214 | def write_timeout_error_hash(error) 215 | hash = execution_error_hash(error) 216 | hash[:type] = error.type 217 | hash[:consistency] = error.consistency 218 | hash[:required] = error.required 219 | hash[:received] = error.received 220 | hash 221 | end 222 | 223 | def unavailable_error_hash(error) 224 | hash = execution_error_hash(error) 225 | hash[:consistency] = error.consistency 226 | hash[:required] = error.required 227 | hash[:alive] = error.alive 228 | hash 229 | end 230 | 231 | def no_hosts_available_error_hash(error) 232 | hash = exception_hash(error) 233 | errors = [] 234 | 235 | error.errors.each do |host, e| 236 | errors << { 237 | :host => host_hash(host), 238 | :error => error_hash(e) 239 | } 240 | end 241 | 242 | hash[:errors] = errors 243 | hash 244 | end 245 | end 246 | end 247 | end 248 | -------------------------------------------------------------------------------- /app/helpers/sse.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class App 4 | module Helpers 5 | module SSE 6 | class Streamer 7 | include Helpers::JSON 8 | 9 | def initialize(out) 10 | @out = out 11 | end 12 | 13 | def host_found(host) 14 | @out << "event: host_found\ndata: #{json_dump(host)}\n\n" 15 | end 16 | 17 | def host_up(host) 18 | @out << "event: host_up\ndata: #{json_dump(host)}\n\n" 19 | end 20 | 21 | def host_down(host) 22 | @out << "event: host_down\ndata: #{json_dump(host)}\n\n" 23 | end 24 | 25 | def host_lost(host) 26 | @out << "event: host_lost\ndata: #{json_dump(host)}\n\n" 27 | end 28 | 29 | def keyspace_created(keyspace) 30 | @out << "event: keyspace_created\ndata: #{json_dump(keyspace)}\n\n" 31 | end 32 | 33 | def keyspace_changed(keyspace) 34 | @out << "event: keyspace_changed\ndata: #{json_dump(keyspace)}\n\n" 35 | end 36 | 37 | def keyspace_dropped(keyspace) 38 | @out << "event: keyspace_dropped\ndata: #{json_dump(keyspace)}\n\n" 39 | end 40 | end 41 | 42 | def stream_events(out) 43 | listener = Streamer.new(out) 44 | heartbeat = EM.add_periodic_timer(2) { out << "\n" } 45 | 46 | cluster.register(listener) 47 | 48 | out.callback do 49 | heartbeat.cancel 50 | cluster.unregister(listener) 51 | end 52 | 53 | out.errback do |error| 54 | heartbeat.cancel 55 | cluster.unregister(listener) 56 | end 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /app/public/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn:active, 33 | .btn.active { 34 | background-image: none; 35 | } 36 | .btn-default { 37 | text-shadow: 0 1px 0 #fff; 38 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 39 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 40 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 41 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 42 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 43 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 44 | background-repeat: repeat-x; 45 | border-color: #dbdbdb; 46 | border-color: #ccc; 47 | } 48 | .btn-default:hover, 49 | .btn-default:focus { 50 | background-color: #e0e0e0; 51 | background-position: 0 -15px; 52 | } 53 | .btn-default:active, 54 | .btn-default.active { 55 | background-color: #e0e0e0; 56 | border-color: #dbdbdb; 57 | } 58 | .btn-default:disabled, 59 | .btn-default[disabled] { 60 | background-color: #e0e0e0; 61 | background-image: none; 62 | } 63 | .btn-primary { 64 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 65 | background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 66 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2)); 67 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); 68 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); 69 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 70 | background-repeat: repeat-x; 71 | border-color: #2b669a; 72 | } 73 | .btn-primary:hover, 74 | .btn-primary:focus { 75 | background-color: #2d6ca2; 76 | background-position: 0 -15px; 77 | } 78 | .btn-primary:active, 79 | .btn-primary.active { 80 | background-color: #2d6ca2; 81 | border-color: #2b669a; 82 | } 83 | .btn-primary:disabled, 84 | .btn-primary[disabled] { 85 | background-color: #2d6ca2; 86 | background-image: none; 87 | } 88 | .btn-success { 89 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 90 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 91 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 92 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 93 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 94 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 95 | background-repeat: repeat-x; 96 | border-color: #3e8f3e; 97 | } 98 | .btn-success:hover, 99 | .btn-success:focus { 100 | background-color: #419641; 101 | background-position: 0 -15px; 102 | } 103 | .btn-success:active, 104 | .btn-success.active { 105 | background-color: #419641; 106 | border-color: #3e8f3e; 107 | } 108 | .btn-success:disabled, 109 | .btn-success[disabled] { 110 | background-color: #419641; 111 | background-image: none; 112 | } 113 | .btn-info { 114 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 115 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 116 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 117 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 118 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 119 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 120 | background-repeat: repeat-x; 121 | border-color: #28a4c9; 122 | } 123 | .btn-info:hover, 124 | .btn-info:focus { 125 | background-color: #2aabd2; 126 | background-position: 0 -15px; 127 | } 128 | .btn-info:active, 129 | .btn-info.active { 130 | background-color: #2aabd2; 131 | border-color: #28a4c9; 132 | } 133 | .btn-info:disabled, 134 | .btn-info[disabled] { 135 | background-color: #2aabd2; 136 | background-image: none; 137 | } 138 | .btn-warning { 139 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 140 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 141 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 142 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 143 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 144 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 145 | background-repeat: repeat-x; 146 | border-color: #e38d13; 147 | } 148 | .btn-warning:hover, 149 | .btn-warning:focus { 150 | background-color: #eb9316; 151 | background-position: 0 -15px; 152 | } 153 | .btn-warning:active, 154 | .btn-warning.active { 155 | background-color: #eb9316; 156 | border-color: #e38d13; 157 | } 158 | .btn-warning:disabled, 159 | .btn-warning[disabled] { 160 | background-color: #eb9316; 161 | background-image: none; 162 | } 163 | .btn-danger { 164 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 165 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 166 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 167 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 168 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 169 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 170 | background-repeat: repeat-x; 171 | border-color: #b92c28; 172 | } 173 | .btn-danger:hover, 174 | .btn-danger:focus { 175 | background-color: #c12e2a; 176 | background-position: 0 -15px; 177 | } 178 | .btn-danger:active, 179 | .btn-danger.active { 180 | background-color: #c12e2a; 181 | border-color: #b92c28; 182 | } 183 | .btn-danger:disabled, 184 | .btn-danger[disabled] { 185 | background-color: #c12e2a; 186 | background-image: none; 187 | } 188 | .thumbnail, 189 | .img-thumbnail { 190 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 191 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 192 | } 193 | .dropdown-menu > li > a:hover, 194 | .dropdown-menu > li > a:focus { 195 | background-color: #e8e8e8; 196 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 197 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 198 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 199 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 200 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 201 | background-repeat: repeat-x; 202 | } 203 | .dropdown-menu > .active > a, 204 | .dropdown-menu > .active > a:hover, 205 | .dropdown-menu > .active > a:focus { 206 | background-color: #357ebd; 207 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 208 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); 209 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); 210 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 211 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 212 | background-repeat: repeat-x; 213 | } 214 | .navbar-default { 215 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 216 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 217 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 218 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 220 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 221 | background-repeat: repeat-x; 222 | border-radius: 4px; 223 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 224 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 225 | } 226 | .navbar-default .navbar-nav > .active > a { 227 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 228 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 229 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3)); 230 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); 231 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); 232 | background-repeat: repeat-x; 233 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 234 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 235 | } 236 | .navbar-brand, 237 | .navbar-nav > li > a { 238 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 239 | } 240 | .navbar-inverse { 241 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 242 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 243 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 244 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 245 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 246 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 247 | background-repeat: repeat-x; 248 | } 249 | .navbar-inverse .navbar-nav > .active > a { 250 | background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); 251 | background-image: -o-linear-gradient(top, #222 0%, #282828 100%); 252 | background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828)); 253 | background-image: linear-gradient(to bottom, #222 0%, #282828 100%); 254 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); 255 | background-repeat: repeat-x; 256 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 257 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 258 | } 259 | .navbar-inverse .navbar-brand, 260 | .navbar-inverse .navbar-nav > li > a { 261 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 262 | } 263 | .navbar-static-top, 264 | .navbar-fixed-top, 265 | .navbar-fixed-bottom { 266 | border-radius: 0; 267 | } 268 | .alert { 269 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 270 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 271 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 272 | } 273 | .alert-success { 274 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 275 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 276 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 277 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 278 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 279 | background-repeat: repeat-x; 280 | border-color: #b2dba1; 281 | } 282 | .alert-info { 283 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 284 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 285 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 286 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 287 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 288 | background-repeat: repeat-x; 289 | border-color: #9acfea; 290 | } 291 | .alert-warning { 292 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 293 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 294 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 295 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 296 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 297 | background-repeat: repeat-x; 298 | border-color: #f5e79e; 299 | } 300 | .alert-danger { 301 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 302 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 303 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 304 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 305 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 306 | background-repeat: repeat-x; 307 | border-color: #dca7a7; 308 | } 309 | .progress { 310 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 311 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 312 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 313 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 314 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 315 | background-repeat: repeat-x; 316 | } 317 | .progress-bar { 318 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); 319 | background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%); 320 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9)); 321 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 322 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 323 | background-repeat: repeat-x; 324 | } 325 | .progress-bar-success { 326 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 327 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 328 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 329 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 330 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 331 | background-repeat: repeat-x; 332 | } 333 | .progress-bar-info { 334 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 335 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 336 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 337 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 338 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 339 | background-repeat: repeat-x; 340 | } 341 | .progress-bar-warning { 342 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 343 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 344 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 345 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 346 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 347 | background-repeat: repeat-x; 348 | } 349 | .progress-bar-danger { 350 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 351 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 352 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 353 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 354 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 355 | background-repeat: repeat-x; 356 | } 357 | .progress-bar-striped { 358 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 359 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 360 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 361 | } 362 | .list-group { 363 | border-radius: 4px; 364 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 365 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 366 | } 367 | .list-group-item.active, 368 | .list-group-item.active:hover, 369 | .list-group-item.active:focus { 370 | text-shadow: 0 -1px 0 #3071a9; 371 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); 372 | background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%); 373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3)); 374 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 376 | background-repeat: repeat-x; 377 | border-color: #3278b3; 378 | } 379 | .panel { 380 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 381 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 382 | } 383 | .panel-default > .panel-heading { 384 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 385 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 386 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 387 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 388 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 389 | background-repeat: repeat-x; 390 | } 391 | .panel-primary > .panel-heading { 392 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 393 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); 394 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); 395 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 396 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 397 | background-repeat: repeat-x; 398 | } 399 | .panel-success > .panel-heading { 400 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 401 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 403 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | .panel-info > .panel-heading { 408 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 409 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 410 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 411 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 412 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 413 | background-repeat: repeat-x; 414 | } 415 | .panel-warning > .panel-heading { 416 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 417 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 418 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 419 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 420 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 421 | background-repeat: repeat-x; 422 | } 423 | .panel-danger > .panel-heading { 424 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 425 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 426 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 427 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 428 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 429 | background-repeat: repeat-x; 430 | } 431 | .well { 432 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 433 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 435 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #dcdcdc; 439 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 440 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 441 | } 442 | /*# sourceMappingURL=bootstrap-theme.css.map */ 443 | -------------------------------------------------------------------------------- /app/public/css/codemirror-solarized.css: -------------------------------------------------------------------------------- 1 | /* 2 | Solarized theme for code-mirror 3 | http://ethanschoonover.com/solarized 4 | */ 5 | 6 | /* 7 | Solarized color pallet 8 | http://ethanschoonover.com/solarized/img/solarized-palette.png 9 | */ 10 | 11 | .solarized.base03 { color: #002b36; } 12 | .solarized.base02 { color: #073642; } 13 | .solarized.base01 { color: #586e75; } 14 | .solarized.base00 { color: #657b83; } 15 | .solarized.base0 { color: #839496; } 16 | .solarized.base1 { color: #93a1a1; } 17 | .solarized.base2 { color: #eee8d5; } 18 | .solarized.base3 { color: #fdf6e3; } 19 | .solarized.solar-yellow { color: #b58900; } 20 | .solarized.solar-orange { color: #cb4b16; } 21 | .solarized.solar-red { color: #dc322f; } 22 | .solarized.solar-magenta { color: #d33682; } 23 | .solarized.solar-violet { color: #6c71c4; } 24 | .solarized.solar-blue { color: #268bd2; } 25 | .solarized.solar-cyan { color: #2aa198; } 26 | .solarized.solar-green { color: #859900; } 27 | 28 | /* Color scheme for code-mirror */ 29 | 30 | .cm-s-solarized { 31 | line-height: 1.45em; 32 | color-profile: sRGB; 33 | rendering-intent: auto; 34 | } 35 | .cm-s-solarized.cm-s-dark { 36 | color: #839496; 37 | background-color: #002b36; 38 | text-shadow: #002b36 0 1px; 39 | } 40 | .cm-s-solarized.cm-s-light { 41 | background-color: #fdf6e3; 42 | color: #657b83; 43 | text-shadow: #eee8d5 0 1px; 44 | } 45 | 46 | .cm-s-solarized .CodeMirror-widget { 47 | text-shadow: none; 48 | } 49 | 50 | 51 | .cm-s-solarized .cm-keyword { color: #cb4b16 } 52 | .cm-s-solarized .cm-atom { color: #d33682; } 53 | .cm-s-solarized .cm-number { color: #d33682; } 54 | .cm-s-solarized .cm-def { color: #2aa198; } 55 | 56 | .cm-s-solarized .cm-variable { color: #268bd2; } 57 | .cm-s-solarized .cm-variable-2 { color: #b58900; } 58 | .cm-s-solarized .cm-variable-3 { color: #6c71c4; } 59 | 60 | .cm-s-solarized .cm-property { color: #2aa198; } 61 | .cm-s-solarized .cm-operator {color: #6c71c4;} 62 | 63 | .cm-s-solarized .cm-comment { color: #586e75; font-style:italic; } 64 | 65 | .cm-s-solarized .cm-string { color: #859900; } 66 | .cm-s-solarized .cm-string-2 { color: #b58900; } 67 | 68 | .cm-s-solarized .cm-meta { color: #859900; } 69 | .cm-s-solarized .cm-qualifier { color: #b58900; } 70 | .cm-s-solarized .cm-builtin { color: #d33682; } 71 | .cm-s-solarized .cm-bracket { color: #cb4b16; } 72 | .cm-s-solarized .CodeMirror-matchingbracket { color: #859900; } 73 | .cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; } 74 | .cm-s-solarized .cm-tag { color: #93a1a1 } 75 | .cm-s-solarized .cm-attribute { color: #2aa198; } 76 | .cm-s-solarized .cm-header { color: #586e75; } 77 | .cm-s-solarized .cm-quote { color: #93a1a1; } 78 | .cm-s-solarized .cm-hr { 79 | color: transparent; 80 | border-top: 1px solid #586e75; 81 | display: block; 82 | } 83 | .cm-s-solarized .cm-link { color: #93a1a1; cursor: pointer; } 84 | .cm-s-solarized .cm-special { color: #6c71c4; } 85 | .cm-s-solarized .cm-em { 86 | color: #999; 87 | text-decoration: underline; 88 | text-decoration-style: dotted; 89 | } 90 | .cm-s-solarized .cm-strong { color: #eee; } 91 | .cm-s-solarized .cm-tab:before { 92 | content: "➤"; /*visualize tab character*/ 93 | color: #586e75; 94 | position:absolute; 95 | } 96 | .cm-s-solarized .cm-error, 97 | .cm-s-solarized .cm-invalidchar { 98 | color: #586e75; 99 | border-bottom: 1px dotted #dc322f; 100 | } 101 | 102 | .cm-s-solarized.cm-s-dark .CodeMirror-selected { 103 | background: #073642; 104 | } 105 | 106 | .cm-s-solarized.cm-s-light .CodeMirror-selected { 107 | background: #eee8d5; 108 | } 109 | 110 | /* Editor styling */ 111 | 112 | 113 | 114 | /* Little shadow on the view-port of the buffer view */ 115 | .cm-s-solarized.CodeMirror { 116 | -moz-box-shadow: inset 7px 0 12px -6px #000; 117 | -webkit-box-shadow: inset 7px 0 12px -6px #000; 118 | box-shadow: inset 7px 0 12px -6px #000; 119 | } 120 | 121 | /* Gutter border and some shadow from it */ 122 | .cm-s-solarized .CodeMirror-gutters { 123 | border-right: 1px solid; 124 | } 125 | 126 | /* Gutter colors and line number styling based of color scheme (dark / light) */ 127 | 128 | /* Dark */ 129 | .cm-s-solarized.cm-s-dark .CodeMirror-gutters { 130 | background-color: #002b36; 131 | border-color: #00232c; 132 | } 133 | 134 | .cm-s-solarized.cm-s-dark .CodeMirror-linenumber { 135 | text-shadow: #021014 0 -1px; 136 | } 137 | 138 | /* Light */ 139 | .cm-s-solarized.cm-s-light .CodeMirror-gutters { 140 | background-color: #fdf6e3; 141 | border-color: #eee8d5; 142 | } 143 | 144 | /* Common */ 145 | .cm-s-solarized .CodeMirror-linenumber { 146 | color: #586e75; 147 | padding: 0 5px; 148 | } 149 | .cm-s-solarized .CodeMirror-guttermarker-subtle { color: #586e75; } 150 | .cm-s-solarized.cm-s-dark .CodeMirror-guttermarker { color: #ddd; } 151 | .cm-s-solarized.cm-s-light .CodeMirror-guttermarker { color: #cb4b16; } 152 | 153 | .cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text { 154 | color: #586e75; 155 | } 156 | 157 | .cm-s-solarized .CodeMirror-lines .CodeMirror-cursor { 158 | border-left: 1px solid #819090; 159 | } 160 | 161 | /* 162 | Active line. Negative margin compensates left padding of the text in the 163 | view-port 164 | */ 165 | .cm-s-solarized.cm-s-dark .CodeMirror-activeline-background { 166 | background: rgba(255, 255, 255, 0.10); 167 | } 168 | .cm-s-solarized.cm-s-light .CodeMirror-activeline-background { 169 | background: rgba(0, 0, 0, 0.10); 170 | } 171 | -------------------------------------------------------------------------------- /app/public/css/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | } 8 | .CodeMirror-scroll { 9 | /* Set scrolling behaviour here */ 10 | overflow: auto; 11 | } 12 | 13 | /* PADDING */ 14 | 15 | .CodeMirror-lines { 16 | padding: 4px 0; /* Vertical padding around content */ 17 | } 18 | .CodeMirror pre { 19 | padding: 0 4px; /* Horizontal padding of content */ 20 | } 21 | 22 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 23 | background-color: white; /* The little square between H and V scrollbars */ 24 | } 25 | 26 | /* GUTTER */ 27 | 28 | .CodeMirror-gutters { 29 | border-right: 1px solid #ddd; 30 | background-color: #f7f7f7; 31 | white-space: nowrap; 32 | } 33 | .CodeMirror-linenumbers {} 34 | .CodeMirror-linenumber { 35 | padding: 0 3px 0 5px; 36 | min-width: 20px; 37 | text-align: right; 38 | color: #999; 39 | -moz-box-sizing: content-box; 40 | box-sizing: content-box; 41 | } 42 | 43 | .CodeMirror-guttermarker { color: black; } 44 | .CodeMirror-guttermarker-subtle { color: #999; } 45 | 46 | /* CURSOR */ 47 | 48 | .CodeMirror div.CodeMirror-cursor { 49 | border-left: 1px solid black; 50 | } 51 | /* Shown when moving in bi-directional text */ 52 | .CodeMirror div.CodeMirror-secondarycursor { 53 | border-left: 1px solid silver; 54 | } 55 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { 56 | width: auto; 57 | border: 0; 58 | background: #7e7; 59 | } 60 | .cm-animate-fat-cursor { 61 | width: auto; 62 | border: 0; 63 | -webkit-animation: blink 1.06s steps(1) infinite; 64 | -moz-animation: blink 1.06s steps(1) infinite; 65 | animation: blink 1.06s steps(1) infinite; 66 | } 67 | @-moz-keyframes blink { 68 | 0% { background: #7e7; } 69 | 50% { background: none; } 70 | 100% { background: #7e7; } 71 | } 72 | @-webkit-keyframes blink { 73 | 0% { background: #7e7; } 74 | 50% { background: none; } 75 | 100% { background: #7e7; } 76 | } 77 | @keyframes blink { 78 | 0% { background: #7e7; } 79 | 50% { background: none; } 80 | 100% { background: #7e7; } 81 | } 82 | 83 | /* Can style cursor different in overwrite (non-insert) mode */ 84 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 85 | 86 | .cm-tab { display: inline-block; } 87 | 88 | .CodeMirror-ruler { 89 | border-left: 1px solid #ccc; 90 | position: absolute; 91 | } 92 | 93 | /* DEFAULT THEME */ 94 | 95 | .cm-s-default .cm-keyword {color: #708;} 96 | .cm-s-default .cm-atom {color: #219;} 97 | .cm-s-default .cm-number {color: #164;} 98 | .cm-s-default .cm-def {color: #00f;} 99 | .cm-s-default .cm-variable, 100 | .cm-s-default .cm-punctuation, 101 | .cm-s-default .cm-property, 102 | .cm-s-default .cm-operator {} 103 | .cm-s-default .cm-variable-2 {color: #05a;} 104 | .cm-s-default .cm-variable-3 {color: #085;} 105 | .cm-s-default .cm-comment {color: #a50;} 106 | .cm-s-default .cm-string {color: #a11;} 107 | .cm-s-default .cm-string-2 {color: #f50;} 108 | .cm-s-default .cm-meta {color: #555;} 109 | .cm-s-default .cm-qualifier {color: #555;} 110 | .cm-s-default .cm-builtin {color: #30a;} 111 | .cm-s-default .cm-bracket {color: #997;} 112 | .cm-s-default .cm-tag {color: #170;} 113 | .cm-s-default .cm-attribute {color: #00c;} 114 | .cm-s-default .cm-header {color: blue;} 115 | .cm-s-default .cm-quote {color: #090;} 116 | .cm-s-default .cm-hr {color: #999;} 117 | .cm-s-default .cm-link {color: #00c;} 118 | 119 | .cm-negative {color: #d44;} 120 | .cm-positive {color: #292;} 121 | .cm-header, .cm-strong {font-weight: bold;} 122 | .cm-em {font-style: italic;} 123 | .cm-link {text-decoration: underline;} 124 | 125 | .cm-s-default .cm-error {color: #f00;} 126 | .cm-invalidchar {color: #f00;} 127 | 128 | /* Default styles for common addons */ 129 | 130 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 131 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 132 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 133 | .CodeMirror-activeline-background {background: #e8f2ff;} 134 | 135 | /* STOP */ 136 | 137 | /* The rest of this file contains styles related to the mechanics of 138 | the editor. You probably shouldn't touch them. */ 139 | 140 | .CodeMirror { 141 | line-height: 1; 142 | position: relative; 143 | overflow: hidden; 144 | background: white; 145 | color: black; 146 | } 147 | 148 | .CodeMirror-scroll { 149 | /* 30px is the magic margin used to hide the element's real scrollbars */ 150 | /* See overflow: hidden in .CodeMirror */ 151 | margin-bottom: -30px; margin-right: -30px; 152 | padding-bottom: 30px; 153 | height: 100%; 154 | outline: none; /* Prevent dragging from highlighting the element */ 155 | position: relative; 156 | -moz-box-sizing: content-box; 157 | box-sizing: content-box; 158 | } 159 | .CodeMirror-sizer { 160 | position: relative; 161 | border-right: 30px solid transparent; 162 | -moz-box-sizing: content-box; 163 | box-sizing: content-box; 164 | } 165 | 166 | /* The fake, visible scrollbars. Used to force redraw during scrolling 167 | before actuall scrolling happens, thus preventing shaking and 168 | flickering artifacts. */ 169 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 170 | position: absolute; 171 | z-index: 6; 172 | display: none; 173 | } 174 | .CodeMirror-vscrollbar { 175 | right: 0; top: 0; 176 | overflow-x: hidden; 177 | overflow-y: scroll; 178 | } 179 | .CodeMirror-hscrollbar { 180 | bottom: 0; left: 0; 181 | overflow-y: hidden; 182 | overflow-x: scroll; 183 | } 184 | .CodeMirror-scrollbar-filler { 185 | right: 0; bottom: 0; 186 | } 187 | .CodeMirror-gutter-filler { 188 | left: 0; bottom: 0; 189 | } 190 | 191 | .CodeMirror-gutters { 192 | position: absolute; left: 0; top: 0; 193 | padding-bottom: 30px; 194 | z-index: 3; 195 | } 196 | .CodeMirror-gutter { 197 | white-space: normal; 198 | height: 100%; 199 | -moz-box-sizing: content-box; 200 | box-sizing: content-box; 201 | padding-bottom: 30px; 202 | margin-bottom: -32px; 203 | display: inline-block; 204 | /* Hack to make IE7 behave */ 205 | *zoom:1; 206 | *display:inline; 207 | } 208 | .CodeMirror-gutter-elt { 209 | position: absolute; 210 | cursor: default; 211 | z-index: 4; 212 | } 213 | 214 | .CodeMirror-lines { 215 | cursor: text; 216 | } 217 | .CodeMirror pre { 218 | /* Reset some styles that the rest of the page might have set */ 219 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 220 | border-width: 0; 221 | background: transparent; 222 | font-family: inherit; 223 | font-size: inherit; 224 | margin: 0; 225 | white-space: pre; 226 | word-wrap: normal; 227 | line-height: inherit; 228 | color: inherit; 229 | z-index: 2; 230 | position: relative; 231 | overflow: visible; 232 | } 233 | .CodeMirror-wrap pre { 234 | word-wrap: break-word; 235 | white-space: pre-wrap; 236 | word-break: normal; 237 | } 238 | 239 | .CodeMirror-linebackground { 240 | position: absolute; 241 | left: 0; right: 0; top: 0; bottom: 0; 242 | z-index: 0; 243 | } 244 | 245 | .CodeMirror-linewidget { 246 | position: relative; 247 | z-index: 2; 248 | overflow: auto; 249 | } 250 | 251 | .CodeMirror-widget {} 252 | 253 | .CodeMirror-wrap .CodeMirror-scroll { 254 | overflow-x: hidden; 255 | } 256 | 257 | .CodeMirror-measure { 258 | position: absolute; 259 | width: 100%; 260 | height: 0; 261 | overflow: hidden; 262 | visibility: hidden; 263 | } 264 | .CodeMirror-measure pre { position: static; } 265 | 266 | .CodeMirror div.CodeMirror-cursor { 267 | position: absolute; 268 | border-right: none; 269 | width: 0; 270 | } 271 | 272 | div.CodeMirror-cursors { 273 | visibility: hidden; 274 | position: relative; 275 | z-index: 1; 276 | } 277 | .CodeMirror-focused div.CodeMirror-cursors { 278 | visibility: visible; 279 | } 280 | 281 | .CodeMirror-selected { background: #d9d9d9; } 282 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 283 | .CodeMirror-crosshair { cursor: crosshair; } 284 | 285 | .cm-searching { 286 | background: #ffa; 287 | background: rgba(255, 255, 0, .4); 288 | } 289 | 290 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 291 | .CodeMirror span { *vertical-align: text-bottom; } 292 | 293 | /* Used to force a border model for a node */ 294 | .cm-force-border { padding-right: .1px; } 295 | 296 | @media print { 297 | /* Hide the cursor when printing */ 298 | .CodeMirror div.CodeMirror-cursors { 299 | visibility: hidden; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /app/public/css/prism.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Name: Base16 Solarized Light 4 | Author: Ethan Schoonover (http://ethanschoonover.com/solarized) 5 | 6 | Prism template by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/prism/) 7 | Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) 8 | 9 | */ 10 | 11 | code[class*="language-"], 12 | pre[class*="language-"] { 13 | color: #fdf6e3; 14 | font-family: Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", Courier, monospace; 15 | font-size: 14px; 16 | line-height: 1.375; 17 | direction: ltr; 18 | text-align: left; 19 | word-spacing: normal; 20 | 21 | -moz-tab-size: 4; 22 | -o-tab-size: 4; 23 | tab-size: 4; 24 | 25 | -webkit-hyphens: none; 26 | -moz-hyphens: none; 27 | -ms-hyphens: none; 28 | hyphens: none; 29 | white-space: pre; 30 | white-space: pre-wrap; 31 | word-break: break-all; 32 | word-wrap: break-word; 33 | background: #fdf6e3; 34 | color: #586e75; 35 | } 36 | 37 | /* Code blocks */ 38 | pre[class*="language-"] { 39 | padding: 1em; 40 | margin: .5em 0; 41 | overflow: auto; 42 | } 43 | 44 | /* Inline code */ 45 | :not(pre) > code[class*="language-"] { 46 | padding: .1em; 47 | border-radius: .3em; 48 | } 49 | 50 | .token.comment, 51 | .token.prolog, 52 | .token.doctype, 53 | .token.cdata { 54 | color: #93a1a1; 55 | } 56 | 57 | .token.punctuation { 58 | color: #586e75; 59 | } 60 | 61 | .namespace { 62 | opacity: .7; 63 | } 64 | 65 | .token.null, 66 | .token.operator, 67 | .token.boolean, 68 | .token.number { 69 | color: #cb4b16; 70 | } 71 | .token.property { 72 | color: #b58900; 73 | } 74 | .token.tag { 75 | color: #268bd2; 76 | } 77 | .token.string { 78 | color: #2aa198; 79 | } 80 | .token.selector { 81 | color: #6c71c4; 82 | } 83 | .token.attr-name { 84 | color: #cb4b16; 85 | } 86 | .token.entity, 87 | .token.url, 88 | .language-css .token.string, 89 | .style .token.string { 90 | color: #2aa198; 91 | } 92 | 93 | .token.attr-value, 94 | .token.keyword, 95 | .token.control, 96 | .token.directive, 97 | .token.unit { 98 | color: #859900; 99 | } 100 | 101 | .token.statement, 102 | .token.regex, 103 | .token.atrule { 104 | color: #2aa198; 105 | } 106 | 107 | .token.placeholder, 108 | .token.variable { 109 | color: #268bd2; 110 | } 111 | 112 | .token.important { 113 | color: #dc322f; 114 | font-weight: bold; 115 | } 116 | 117 | .token.entity { 118 | cursor: help; 119 | } 120 | 121 | pre > code.highlight { 122 | outline: .4em solid red; 123 | outline-offset: .4em; 124 | } 125 | -------------------------------------------------------------------------------- /app/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-top: 50px; 3 | } 4 | 5 | .nav, 6 | .pagination, 7 | .carousel, 8 | .panel-title a { 9 | cursor: pointer; 10 | } 11 | 12 | .statement-editor { 13 | padding: 0; 14 | } 15 | 16 | .highlight { 17 | background-color: #fdf6e3; 18 | } 19 | 20 | th.row-actions { 21 | width: 145px; 22 | } 23 | 24 | th.table-actions { 25 | width: 100px; 26 | } 27 | 28 | .modal-content .rows { 29 | overflow: auto; 30 | } 31 | 32 | .execute-options { 33 | display: block !important; 34 | padding: 5px; 35 | } 36 | 37 | .row-limit { 38 | margin: 15px 0 15px 0; 39 | } 40 | 41 | .row-limit > input { 42 | text-align: right; 43 | border: 1px solid #ddd; 44 | } 45 | 46 | .row-limit > label { 47 | padding-right: 5px; 48 | } 49 | 50 | .loading-button { 51 | margin-top: 15px; 52 | } 53 | 54 | .glyphicon-refresh-animate { 55 | -animation: spin .7s infinite linear; 56 | -webkit-animation: spin2 .7s infinite linear; 57 | } 58 | 59 | @-webkit-keyframes spin2 { 60 | from { 61 | -webkit-transform: rotate(0deg); 62 | } 63 | to { 64 | -webkit-transform: rotate(360deg); 65 | } 66 | } 67 | 68 | @keyframes spin { 69 | from { 70 | transform: scale(1) rotate(0deg); 71 | } 72 | to { 73 | transform: scale(1) rotate(360deg); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avalanche123/cassandra-web/f11e47a26f316827f631d7bcfec14b9dd94f44be/app/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/public/fonts/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 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 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /app/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avalanche123/cassandra-web/f11e47a26f316827f631d7bcfec14b9dd94f44be/app/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avalanche123/cassandra-web/f11e47a26f316827f631d7bcfec14b9dd94f44be/app/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/public/html/404.html: -------------------------------------------------------------------------------- 1 |

Not Found

2 | -------------------------------------------------------------------------------- /app/public/html/execute.html: -------------------------------------------------------------------------------- 1 | 5 | 96 | 113 | -------------------------------------------------------------------------------- /app/public/html/index.html: -------------------------------------------------------------------------------- 1 |

Cluster Status

2 |
3 |

Hosts

4 |
5 |
Datacenter: {{datacenter}}
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
ipidrackversionstatus
{{host.ip}}{{host.id}}{{host.rack}}{{host.release_version}}{{host.status}}
27 |
28 |
29 |
30 |
31 |

Keyspaces

32 |
33 |
34 | {{keyspace.name}} 35 |
36 |
37 |
38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /app/public/html/keyspace.html: -------------------------------------------------------------------------------- 1 |

Keyspace: {{keyspace.name}}

2 | 3 | 4 | Tables 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | 32 |
NameActions
{{table.name}} 16 | 17 | 18 |
Total: {{keyspace.tables.length}} tables 25 | 29 |
33 |
34 | 35 | Definition 36 |

37 |

38 |

39 |
40 |
41 | -------------------------------------------------------------------------------- /app/public/html/table.html: -------------------------------------------------------------------------------- 1 |

Table: {{keyspace.name}}.{{table.name}}

2 | 3 | 4 | Rows {{result.rows.length}} 5 | 6 |
7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 |
15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 39 | 40 | 41 | 42 | 43 | 50 | 53 | 54 | 55 |
{{column.name}}Actions
31 | {{row[column.name]}} 32 | 34 |
35 | 36 | 37 |
38 |
44 | Total: {{result.rows.length}} results 45 |
46 |
{{error.class}}: {{error.message}}
47 |
{{entry}}
48 |
49 |
51 | 52 |
56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
NameTypeOrderOptions
{{column.name}}{{column.type}}{{column.order}}{{column.options}}
76 |
77 | 78 |

79 |

80 |

81 |
82 |
83 | -------------------------------------------------------------------------------- /app/public/js/angular-filter.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bunch of useful filters for angularJS 3 | * @version v0.4.5 - 2014-09-08 * @link https://github.com/a8m/angular-filter 4 | * @author Ariel Mashraki 5 | * @license MIT License, http://www.opensource.org/licenses/MIT 6 | */!function(a,b,c){"use strict";function d(a){return s(a)?a:Object.keys(a).map(function(b){return a[b]})}function e(a){return null===a}function f(a,b){var c=Object.keys(a);return-1==c.map(function(c){return!(!b[c]||b[c]!=a[c])}).indexOf(!1)}function g(a,b){if(""===b)return a;var c=a.indexOf(b.charAt(0));return-1===c?!1:g(a.substr(c+1),b.substr(1))}function h(a,b,c){var d,e=~~d;return d=a.filter(function(a){var d=m(c)?b>e&&c(a):b>e;return e=d?e+1:e,d}),2>e?d[0]:d}function i(a){return function(b,c){return b=r(b)?d(b):b,!s(b)||n(c)?!0:b.some(function(b){return r(b)||o(c)?a(c)(b):b===c})}}function j(a,b){return b=b||0,b>=a.length?a:s(a[b])?j(a.slice(0,b).concat(a[b],a.slice(b+1)),b):j(a,b+1)}function k(a){return function(b,c){function e(a,b){return n(b)?!1:a.some(function(a){return w(a,b)})}if(b=r(b)?d(b):b,!s(b))return b;var f=[],g=a(c);return b.filter(n(c)?function(a,b,c){return c.indexOf(a)===b}:function(a){var b=g(a);return e(f,b)?!1:(f.push(b),!0)})}}function l(a,b,c){return b?a+c+l(a,--b,c):a}var m=b.isDefined,n=b.isUndefined,o=b.isFunction,p=b.isString,q=b.isNumber,r=b.isObject,s=b.isArray,t=b.forEach,u=b.extend,v=b.copy,w=b.equals;String.prototype.contains||(String.prototype.contains=function(){return-1!==String.prototype.indexOf.apply(this,arguments)}),b.module("a8m.angular",[]).filter("isUndefined",function(){return function(a){return b.isUndefined(a)}}).filter("isDefined",function(){return function(a){return b.isDefined(a)}}).filter("isFunction",function(){return function(a){return b.isFunction(a)}}).filter("isString",function(){return function(a){return b.isString(a)}}).filter("isNumber",function(){return function(a){return b.isNumber(a)}}).filter("isArray",function(){return function(a){return b.isArray(a)}}).filter("isObject",function(){return function(a){return b.isObject(a)}}).filter("isEqual",function(){return function(a,c){return b.equals(a,c)}}),b.module("a8m.is-null",[]).filter("isNull",function(){return function(a){return e(a)}}),b.module("a8m.after-where",[]).filter("afterWhere",function(){return function(a,b){if(a=r(a)?d(a):a,!s(a)||n(b))return a;var c=a.map(function(a){return f(b,a)}).indexOf(!0);return a.slice(-1===c?0:c)}}),b.module("a8m.after",[]).filter("after",function(){return function(a,b){return a=r(a)?d(a):a,s(a)?a.slice(b):a}}),b.module("a8m.before-where",[]).filter("beforeWhere",function(){return function(a,b){if(a=r(a)?d(a):a,!s(a)||n(b))return a;var c=a.map(function(a){return f(b,a)}).indexOf(!0);return a.slice(0,-1===c?a.length:++c)}}),b.module("a8m.before",[]).filter("before",function(){return function(a,b){return a=r(a)?d(a):a,s(a)?a.slice(0,b?--b:b):a}}),b.module("a8m.concat",[]).filter("concat",[function(){return function(a,b){if(n(b))return a;if(s(a))return a.concat(r(b)?d(b):b);if(r(a)){var c=d(a);return c.concat(r(b)?d(b):b)}return a}}]),b.module("a8m.contains",[]).filter({contains:["$parse",i],some:["$parse",i]}),b.module("a8m.count-by",[]).filter("countBy",["$parse",function(a){return function(b,c){var e,f={},g=a(c);return b=r(b)?d(b):b,!s(b)||n(c)?b:(b.forEach(function(a){e=g(a),f[e]||(f[e]=0),f[e]++}),f)}}]),b.module("a8m.every",[]).filter("every",["$parse",function(a){return function(b,c){return b=r(b)?d(b):b,!s(b)||n(c)?!0:b.every(function(b){return r(b)||o(c)?a(c)(b):b===c})}}]),b.module("a8m.filter-by",[]).filter("filterBy",["$parse",function(a){return function(b,e,f){var g;return f=p(f)||q(f)?String(f).toLowerCase():c,b=r(b)?d(b):b,!s(b)||n(f)?b:b.filter(function(b){return e.some(function(c){if(~c.indexOf("+")){var d=c.replace(new RegExp("\\s","g"),"").split("+");g=d.reduce(function(c,d,e){return 1===e?a(c)(b)+" "+a(d)(b):c+" "+a(d)(b)})}else g=a(c)(b);return p(g)||q(g)?String(g).toLowerCase().contains(f):!1})})}}]),b.module("a8m.first",[]).filter("first",["$parse",function(a){return function(b){var e,f,g;return b=r(b)?d(b):b,s(b)?(g=Array.prototype.slice.call(arguments,1),e=q(g[0])?g[0]:1,f=q(g[0])?q(g[1])?c:g[1]:g[0],h(b,e,f?a(f):f)):b}}]),b.module("a8m.flatten",[]).filter("flatten",function(){return function(a,b){return b=b||!1,a=r(a)?d(a):a,s(a)?b?[].concat.apply([],a):j(a,0):a}}),b.module("a8m.fuzzy-by",[]).filter("fuzzyBy",["$parse",function(a){return function(b,c,e,f){var h,i,j=f||!1;return b=r(b)?d(b):b,!s(b)||n(c)||n(e)?b:(i=a(c),b.filter(function(a){return h=i(a),p(h)?(h=j?h:h.toLowerCase(),e=j?e:e.toLowerCase(),g(h,e)!==!1):!1}))}}]),b.module("a8m.fuzzy",[]).filter("fuzzy",function(){return function(a,b,c){function e(a,b){var c,d,e=Object.keys(a);return 0<]*>/g,""):a}}),b.module("a8m.trim",[]).filter("trim",function(){return function(a,b){var c=b||"\\s";return p(a)?a.replace(new RegExp("^"+c+"+|"+c+"+$","g"),""):a}}),b.module("a8m.truncate",[]).filter("truncate",function(){return function(a,b,c,d){return b=n(b)?a.length:b,d=d||!1,c=c||"",!p(a)||a.length<=b?a:a.substring(0,d?-1===a.indexOf(" ",b)?a.length:a.indexOf(" ",b):b)+c}}),b.module("a8m.ucfirst",[]).filter("ucfirst",[function(){return function(a){return b.isString(a)?a.split(" ").map(function(a){return a.charAt(0).toUpperCase()+a.substring(1)}).join(" "):a}}]),b.module("a8m.uri-encode",[]).filter("uriEncode",["$window",function(a){return function(b){return p(b)?a.encodeURI(b):b}}]),b.module("a8m.wrap",[]).filter("wrap",function(){return function(a,b,c){return!p(a)||n(b)?a:[b,a,c||b].join("")}}),b.module("a8m.filter-watcher",[]).provider("filterWatcher",function(){var a="_$$";this.setPrefix=function(b){return a=b,this},this.$get=["$window",function(b){function c(b){return a+b}function d(a,b){return m(b[a])}function e(a,b){var e=c(a);return d(e,b)||Object.defineProperty(b,e,{enumerable:!1,configurable:!0,value:{}}),b[e]}function f(a,b){return g(function(){delete b[c(a)]})}var g=b.setTimeout;return{$watch:e,$destroy:f}}]}),b.module("angular.filter",["a8m.ucfirst","a8m.uri-encode","a8m.slugify","a8m.strip-tags","a8m.stringular","a8m.truncate","a8m.starts-with","a8m.ends-with","a8m.wrap","a8m.trim","a8m.ltrim","a8m.rtrim","a8m.repeat","a8m.to-array","a8m.concat","a8m.contains","a8m.unique","a8m.is-empty","a8m.after","a8m.after-where","a8m.before","a8m.before-where","a8m.where","a8m.reverse","a8m.remove","a8m.remove-with","a8m.group-by","a8m.count-by","a8m.search-field","a8m.fuzzy-by","a8m.fuzzy","a8m.omit","a8m.pick","a8m.every","a8m.filter-by","a8m.xor","a8m.map","a8m.first","a8m.last","a8m.flatten","a8m.math","a8m.math.max","a8m.math.min","a8m.math.percent","a8m.math.radix","a8m.math.sum","a8m.angular","a8m.is-null","a8m.filter-watcher"])}(window,window.angular); -------------------------------------------------------------------------------- /app/public/js/cassandra.js: -------------------------------------------------------------------------------- 1 | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys 2 | if (!Object.keys) { 3 | Object.keys = (function () { 4 | 'use strict'; 5 | var hasOwnProperty = Object.prototype.hasOwnProperty, 6 | hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), 7 | dontEnums = [ 8 | 'toString', 9 | 'toLocaleString', 10 | 'valueOf', 11 | 'hasOwnProperty', 12 | 'isPrototypeOf', 13 | 'propertyIsEnumerable', 14 | 'constructor' 15 | ], 16 | dontEnumsLength = dontEnums.length; 17 | 18 | return function (obj) { 19 | if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { 20 | throw new TypeError('Object.keys called on non-object'); 21 | } 22 | 23 | var result = [], prop, i; 24 | 25 | for (prop in obj) { 26 | if (hasOwnProperty.call(obj, prop)) { 27 | result.push(prop); 28 | } 29 | } 30 | 31 | if (hasDontEnumBug) { 32 | for (i = 0; i < dontEnumsLength; i++) { 33 | if (hasOwnProperty.call(obj, dontEnums[i])) { 34 | result.push(dontEnums[i]); 35 | } 36 | } 37 | } 38 | return result; 39 | }; 40 | }()); 41 | } 42 | 43 | !(function(app) { 44 | app.config([ 45 | '$routeProvider', 46 | '$locationProvider', 47 | '$rootScopeProvider', 48 | '$logProvider', 49 | '$compileProvider', 50 | function($routeProvider, $locationProvider, $rootScopeProvider, $logProvider, $compileProvider) { 51 | $routeProvider 52 | .when('/', { 53 | templateUrl: '/html/index.html', 54 | }) 55 | .when('/:keyspace', { 56 | templateUrl: '/html/keyspace.html', 57 | controller: 'keyspace', 58 | resolve: {cluster: 'cluster'} 59 | }) 60 | .when('/:keyspace/:table', { 61 | templateUrl: '/html/table.html', 62 | controller: 'table', 63 | resolve: {cluster: 'cluster'} 64 | }) 65 | .otherwise({redirect:'/'}) 66 | 67 | $locationProvider.html5Mode(true) 68 | $rootScopeProvider.digestTtl(10000) 69 | $logProvider.debugEnabled(true) 70 | $compileProvider.debugInfoEnabled(true) 71 | } 72 | ]) 73 | 74 | app.factory('cluster', [ 75 | '$http', 76 | '$rootScope', 77 | '$q', 78 | function($http, $rootScope, $q) { 79 | var cluster = { 80 | keyspaces: null, 81 | hosts: null, 82 | keyspace: null, 83 | execute: function(statement, options) { 84 | var deferred = $q.defer(); 85 | options = options || {} 86 | 87 | $http({ 88 | method: 'POST', 89 | url: '/execute', 90 | data: { 91 | statement: statement, 92 | options: options 93 | } 94 | }) 95 | .success(function(data, status, headers, config) { 96 | deferred.resolve(data) 97 | }) 98 | .error(function(data, status, headers, config) { 99 | deferred.reject(data) 100 | }) 101 | 102 | return deferred.promise 103 | } 104 | } 105 | var resolved = false 106 | var deferred = $q.defer() 107 | 108 | var events = new EventSource('/events'); 109 | 110 | events.onopen = function(e) { 111 | updateCluster(); 112 | return false; 113 | } 114 | 115 | $rootScope.$on('$destroy', function() { 116 | events.close(); 117 | }) 118 | 119 | var updateCluster = function() { 120 | $http({method: 'GET', url: '/keyspaces'}) 121 | .success(function(data, status, headers, config) { 122 | cluster.keyspaces = data 123 | 124 | events.addEventListener('keyspace_created', addKeyspace, false); 125 | events.addEventListener('keyspace_changed', updateKeyspace, false); 126 | events.addEventListener('keyspace_dropped', removeKeyspace, false); 127 | 128 | if (!resolved && cluster.keyspaces && cluster.hosts) { 129 | resolved = true 130 | deferred.resolve(cluster) 131 | } 132 | }) 133 | .error(function(data, status, headers, config) { 134 | console.log('ERROR', data); 135 | }) 136 | 137 | $http({method: 'GET', url: '/hosts'}) 138 | .success(function(data, status, headers, config) { 139 | cluster.hosts = data 140 | 141 | events.addEventListener('host_found', addHost, false); 142 | events.addEventListener('host_up', updateHost, false); 143 | events.addEventListener('host_down', updateHost, false); 144 | events.addEventListener('host_lost', removeHost, false); 145 | 146 | if (!resolved && cluster.keyspaces && cluster.hosts) { 147 | resolved = true 148 | deferred.resolve(cluster) 149 | } 150 | }) 151 | .error(function(data, status, headers, config) { 152 | console.log('ERROR', data); 153 | }) 154 | } 155 | 156 | var addHost = function(event) { 157 | var host = JSON.parse(event.data) 158 | 159 | $rootScope.$apply(function() { 160 | $rootScope.$broadcast('host.created', host) 161 | cluster.hosts.push(host) 162 | }) 163 | } 164 | 165 | var updateHost = function(event) { 166 | var host = JSON.parse(event.data) 167 | 168 | $rootScope.$apply(function() { 169 | $rootScope.$broadcast('host.updated', host) 170 | angular.forEach(cluster.hosts, function(h, index) { 171 | if (host.ip == h.ip) { 172 | angular.forEach(host, function(value, key) { 173 | cluster.hosts[index][key] = value 174 | }) 175 | } 176 | }) 177 | }) 178 | } 179 | 180 | var removeHost = function(event) { 181 | var host = JSON.parse(event.data) 182 | 183 | $rootScope.$apply(function() { 184 | $rootScope.$broadcast('host.removed', host) 185 | angular.forEach(cluster.hosts, function(h, index) { 186 | if (host.ip == h.ip) { 187 | cluster.hosts.splice(index, 1) 188 | } 189 | }) 190 | }) 191 | } 192 | 193 | var addKeyspace = function(event) { 194 | var keyspace = JSON.parse(event.data) 195 | 196 | $rootScope.$apply(function() { 197 | $rootScope.$broadcast('keyspace.created', keyspace) 198 | cluster.keyspaces.push(keyspace) 199 | }) 200 | } 201 | 202 | var updateKeyspace = function(event) { 203 | var keyspace = JSON.parse(event.data) 204 | 205 | $rootScope.$apply(function() { 206 | $rootScope.$broadcast('keyspace.updated', keyspace) 207 | angular.forEach(cluster.keyspaces, function(k, index) { 208 | if (keyspace.name == k.name) { 209 | angular.forEach(keyspace, function(value, key) { 210 | cluster.keyspaces[index][key] = value 211 | }) 212 | } 213 | }) 214 | }) 215 | } 216 | 217 | var removeKeyspace = function(event) { 218 | var keyspace = JSON.parse(event.data) 219 | 220 | $rootScope.$apply(function() { 221 | $rootScope.$broadcast('keyspace.removed', keyspace) 222 | angular.forEach(cluster.keyspaces, function(k, index) { 223 | if (keyspace.name == k.name) { 224 | cluster.keyspaces.splice(index, 1) 225 | } 226 | }) 227 | }) 228 | } 229 | 230 | return deferred.promise 231 | } 232 | ]) 233 | 234 | app.factory('escapeIdentifier', [ 235 | function() { 236 | var re = /[a-z][a-z0-9_]*/ 237 | 238 | return function(id) { 239 | var matches = id.match(re) 240 | if (matches.length == 1 && matches[0].length == id.length) { 241 | return id 242 | } 243 | return '"' + id + '"' 244 | } 245 | } 246 | ]) 247 | 248 | app.directive('codeHighlight',[ 249 | '$interpolate', 250 | '$compile', 251 | function ($interpolate, $compile) { 252 | return { 253 | restrict: 'E', 254 | template: '', 255 | replace: true, 256 | transclude: true, 257 | link: function (scope, el, attrs) { 258 | var language = Prism.languages[attrs.language] 259 | 260 | attrs.$observe('code', function(code) { 261 | el.html(Prism.highlight(code, language)) 262 | }) 263 | } 264 | } 265 | } 266 | ]) 267 | 268 | app.controller('main', [ 269 | '$scope', 270 | 'cluster', 271 | '$routeParams', 272 | '$modal', 273 | function($scope, cluster, $routeParams, $modal) { 274 | cluster.then( 275 | function(cluster) { 276 | $scope.cluster = cluster 277 | } 278 | ) 279 | 280 | $scope.host_status_class = function(status) { 281 | if (status == 'up') { 282 | return 'success' 283 | } 284 | 285 | return 'danger' 286 | } 287 | 288 | $scope.keyspace_class = function(keyspace) { 289 | if ($routeParams.keyspace && $routeParams.keyspace == keyspace.name) { 290 | return 'active' 291 | } 292 | } 293 | 294 | $scope.show_execute_form = function(statement) { 295 | var childScope = $scope.$new() 296 | 297 | childScope.statement = {cql: statement} 298 | 299 | return $modal.open({ 300 | templateUrl: '/html/execute.html', 301 | controller: 'execute', 302 | size: 'lg', 303 | scope: childScope 304 | }) 305 | } 306 | } 307 | ]) 308 | 309 | app.controller('execute', [ 310 | '$scope', 311 | function($scope) { 312 | $scope.disabled = false 313 | $scope.options = $scope.options || { 314 | trace: false, 315 | consistency: 'one' 316 | } 317 | $scope.statement = $scope.statement || { 318 | cql: '' 319 | } 320 | $scope.consistencies = [ 321 | "any", 322 | "one", 323 | "two", 324 | "three", 325 | "quorum", 326 | "all", 327 | "local_quorum", 328 | "each_quorum", 329 | "serial", 330 | "local_serial", 331 | "local_one" 332 | ] 333 | 334 | $scope.execute = function() { 335 | $scope.disabled = true 336 | 337 | $scope.cluster.execute($scope.statement.cql, $scope.options) 338 | .then( 339 | function(result) { 340 | $scope.disabled = false 341 | $scope.result = result; 342 | }, 343 | function(error) { 344 | $scope.error = error 345 | } 346 | ) 347 | } 348 | 349 | $scope.$watch('options.consistency', function(consistency) { 350 | if ($scope.error) { 351 | $scope.disabled = false 352 | delete $scope.error 353 | } 354 | }) 355 | 356 | $scope.$watch('statement.cql', function(cql) { 357 | if ($scope.error) { 358 | $scope.disabled = false 359 | delete $scope.error 360 | } 361 | }) 362 | } 363 | ]) 364 | 365 | app.controller('keyspace', [ 366 | '$scope', 367 | 'cluster', 368 | '$routeParams', 369 | '$location', 370 | function($scope, cluster, $routeParams, $location) { 371 | cluster.keyspaces.forEach(function(keyspace) { 372 | if ($routeParams.keyspace == keyspace.name) { 373 | $scope.keyspace = keyspace 374 | } 375 | }) 376 | 377 | if (!$scope.keyspace) { 378 | $location.path('/') 379 | } 380 | 381 | $scope.$on('keyspace.removed', function(event, keyspace) { 382 | if ($routeParams.keyspace == keyspace.name) { 383 | $location.path('/') 384 | } 385 | }) 386 | } 387 | ]) 388 | 389 | app.controller('table', [ 390 | '$scope', 391 | 'cluster', 392 | '$routeParams', 393 | '$location', 394 | 'escapeIdentifier', 395 | function($scope, cluster, $routeParams, $location, escapeIdentifier) { 396 | 397 | $scope.rowLimit = { 398 | enabled: true, 399 | value: 15 400 | }; 401 | $scope.loading = false; 402 | 403 | cluster.keyspaces.forEach(function(keyspace) { 404 | if ($routeParams.keyspace == keyspace.name) { 405 | $scope.keyspace = keyspace 406 | } 407 | }) 408 | 409 | if (!$scope.keyspace) { 410 | $location.path('/') 411 | } 412 | 413 | $scope.$on('keyspace.removed', function(event, keyspace) { 414 | if ($routeParams.keyspace == keyspace.name) { 415 | $location.path('/') 416 | } 417 | }) 418 | 419 | $scope.keyspace.tables.forEach(function(table) { 420 | if ($routeParams.table == table.name) { 421 | $scope.table = table 422 | } 423 | }) 424 | 425 | if (!$scope.table) { 426 | $location.path('/') 427 | } 428 | 429 | $scope.$on('keyspace.updated', function(keyspace) { 430 | if ($routeParams.keyspace != keyspace.name) { 431 | return 432 | } 433 | 434 | var exists = false; 435 | 436 | keyspace.tables.forEach(function(table) { 437 | exists = exists || (table.name == $scope.table.name) 438 | }) 439 | 440 | if (!exists) { 441 | $location.path('/' + $scope.keyspace.name) 442 | } 443 | }); 444 | 445 | $scope.executeStatement = function() { 446 | $scope.result = undefined; 447 | $scope.loading = true; 448 | 449 | var selectStatement = 'SELECT * FROM ' + escapeIdentifier($scope.keyspace.name) + '.' + escapeIdentifier($scope.table.name); 450 | if ($scope.rowLimit.enabled) { 451 | selectStatement = selectStatement + ' LIMIT ' + $scope.rowLimit.value; 452 | } 453 | 454 | cluster.execute(selectStatement) 455 | .then( 456 | function(result) { 457 | $scope.result = result; 458 | $scope.loading = false; 459 | }, 460 | function(error) { 461 | $scope.error = error; 462 | $scope.loading = false; 463 | } 464 | ); 465 | }; 466 | 467 | $scope.executeStatement(); 468 | 469 | } 470 | ]); 471 | 472 | app.controller('actions', [ 473 | '$scope', 474 | 'cluster', 475 | 'escapeIdentifier', 476 | function($scope, cluster, escapeIdentifier) { 477 | $scope.disabled = false 478 | $scope.drop = function(keyspaceName, tableName) { 479 | $scope.disabled = true 480 | 481 | return cluster 482 | .then( 483 | function(cluster) { 484 | return cluster 485 | .execute('DROP TABLE ' + escapeIdentifier($scope.keyspace.name) + '.' + escapeIdentifier(tableName)) 486 | } 487 | ) 488 | .then( 489 | function(result) { 490 | $scope.result = result 491 | $scope.disabled = false 492 | }, 493 | function(error) { 494 | $scope.error = error 495 | } 496 | ) 497 | } 498 | } 499 | ]) 500 | })(angular.module('cassandra', ['ngRoute', 'angular.filter', 'ui.bootstrap', 'ui.codemirror'])) 501 | -------------------------------------------------------------------------------- /app/public/js/codemirror-sql.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineMode("sql", function(config, parserConfig) { 15 | "use strict"; 16 | 17 | var client = parserConfig.client || {}, 18 | atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, 19 | builtin = parserConfig.builtin || {}, 20 | keywords = parserConfig.keywords || {}, 21 | operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/, 22 | support = parserConfig.support || {}, 23 | hooks = parserConfig.hooks || {}, 24 | dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true}; 25 | 26 | function tokenBase(stream, state) { 27 | var ch = stream.next(); 28 | 29 | // call hooks from the mime type 30 | if (hooks[ch]) { 31 | var result = hooks[ch](stream, state); 32 | if (result !== false) return result; 33 | } 34 | 35 | if (support.hexNumber == true && 36 | ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) 37 | || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) { 38 | // hex 39 | // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html 40 | return "number"; 41 | } else if (support.binaryNumber == true && 42 | (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/)) 43 | || (ch == "0" && stream.match(/^b[01]+/)))) { 44 | // bitstring 45 | // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html 46 | return "number"; 47 | } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) { 48 | // numbers 49 | // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html 50 | stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/); 51 | support.decimallessFloat == true && stream.eat('.'); 52 | return "number"; 53 | } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) { 54 | // placeholders 55 | return "variable-3"; 56 | } else if (ch == "'" || (ch == '"' && support.doubleQuote)) { 57 | // strings 58 | // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html 59 | state.tokenize = tokenLiteral(ch); 60 | return state.tokenize(stream, state); 61 | } else if ((((support.nCharCast == true && (ch == "n" || ch == "N")) 62 | || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i))) 63 | && (stream.peek() == "'" || stream.peek() == '"'))) { 64 | // charset casting: _utf8'str', N'str', n'str' 65 | // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html 66 | return "keyword"; 67 | } else if (/^[\(\),\;\[\]]/.test(ch)) { 68 | // no highlightning 69 | return null; 70 | } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) { 71 | // 1-line comment 72 | stream.skipToEnd(); 73 | return "comment"; 74 | } else if ((support.commentHash && ch == "#") 75 | || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) { 76 | // 1-line comments 77 | // ref: https://kb.askmonty.org/en/comment-syntax/ 78 | stream.skipToEnd(); 79 | return "comment"; 80 | } else if (ch == "/" && stream.eat("*")) { 81 | // multi-line comments 82 | // ref: https://kb.askmonty.org/en/comment-syntax/ 83 | state.tokenize = tokenComment; 84 | return state.tokenize(stream, state); 85 | } else if (ch == ".") { 86 | // .1 for 0.1 87 | if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) { 88 | return "number"; 89 | } 90 | // .table_name (ODBC) 91 | // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html 92 | if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) { 93 | return "variable-2"; 94 | } 95 | } else if (operatorChars.test(ch)) { 96 | // operators 97 | stream.eatWhile(operatorChars); 98 | return null; 99 | } else if (ch == '{' && 100 | (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) { 101 | // dates (weird ODBC syntax) 102 | // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html 103 | return "number"; 104 | } else { 105 | stream.eatWhile(/^[_\w\d]/); 106 | var word = stream.current().toLowerCase(); 107 | // dates (standard SQL syntax) 108 | // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html 109 | if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/))) 110 | return "number"; 111 | if (atoms.hasOwnProperty(word)) return "atom"; 112 | if (builtin.hasOwnProperty(word)) return "builtin"; 113 | if (keywords.hasOwnProperty(word)) return "keyword"; 114 | if (client.hasOwnProperty(word)) return "string-2"; 115 | return null; 116 | } 117 | } 118 | 119 | // 'string', with char specified in quote escaped by '\' 120 | function tokenLiteral(quote) { 121 | return function(stream, state) { 122 | var escaped = false, ch; 123 | while ((ch = stream.next()) != null) { 124 | if (ch == quote && !escaped) { 125 | state.tokenize = tokenBase; 126 | break; 127 | } 128 | escaped = !escaped && ch == "\\"; 129 | } 130 | return "string"; 131 | }; 132 | } 133 | function tokenComment(stream, state) { 134 | while (true) { 135 | if (stream.skipTo("*")) { 136 | stream.next(); 137 | if (stream.eat("/")) { 138 | state.tokenize = tokenBase; 139 | break; 140 | } 141 | } else { 142 | stream.skipToEnd(); 143 | break; 144 | } 145 | } 146 | return "comment"; 147 | } 148 | 149 | function pushContext(stream, state, type) { 150 | state.context = { 151 | prev: state.context, 152 | indent: stream.indentation(), 153 | col: stream.column(), 154 | type: type 155 | }; 156 | } 157 | 158 | function popContext(state) { 159 | state.indent = state.context.indent; 160 | state.context = state.context.prev; 161 | } 162 | 163 | return { 164 | startState: function() { 165 | return {tokenize: tokenBase, context: null}; 166 | }, 167 | 168 | token: function(stream, state) { 169 | if (stream.sol()) { 170 | if (state.context && state.context.align == null) 171 | state.context.align = false; 172 | } 173 | if (stream.eatSpace()) return null; 174 | 175 | var style = state.tokenize(stream, state); 176 | if (style == "comment") return style; 177 | 178 | if (state.context && state.context.align == null) 179 | state.context.align = true; 180 | 181 | var tok = stream.current(); 182 | if (tok == "(") 183 | pushContext(stream, state, ")"); 184 | else if (tok == "[") 185 | pushContext(stream, state, "]"); 186 | else if (state.context && state.context.type == tok) 187 | popContext(state); 188 | return style; 189 | }, 190 | 191 | indent: function(state, textAfter) { 192 | var cx = state.context; 193 | if (!cx) return 0; 194 | var closing = textAfter.charAt(0) == cx.type; 195 | if (cx.align) return cx.col + (closing ? 0 : 1); 196 | else return cx.indent + (closing ? 0 : config.indentUnit); 197 | }, 198 | 199 | blockCommentStart: "/*", 200 | blockCommentEnd: "*/", 201 | lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null 202 | }; 203 | }); 204 | 205 | (function() { 206 | "use strict"; 207 | 208 | // `identifier` 209 | function hookIdentifier(stream) { 210 | // MySQL/MariaDB identifiers 211 | // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html 212 | var ch; 213 | while ((ch = stream.next()) != null) { 214 | if (ch == "`" && !stream.eat("`")) return "variable-2"; 215 | } 216 | return null; 217 | } 218 | 219 | // variable token 220 | function hookVar(stream) { 221 | // variables 222 | // @@prefix.varName @varName 223 | // varName can be quoted with ` or ' or " 224 | // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html 225 | if (stream.eat("@")) { 226 | stream.match(/^session\./); 227 | stream.match(/^local\./); 228 | stream.match(/^global\./); 229 | } 230 | 231 | if (stream.eat("'")) { 232 | stream.match(/^.*'/); 233 | return "variable-2"; 234 | } else if (stream.eat('"')) { 235 | stream.match(/^.*"/); 236 | return "variable-2"; 237 | } else if (stream.eat("`")) { 238 | stream.match(/^.*`/); 239 | return "variable-2"; 240 | } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) { 241 | return "variable-2"; 242 | } 243 | return null; 244 | }; 245 | 246 | // short client keyword token 247 | function hookClient(stream) { 248 | // \N means NULL 249 | // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html 250 | if (stream.eat("N")) { 251 | return "atom"; 252 | } 253 | // \g, etc 254 | // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html 255 | return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null; 256 | } 257 | 258 | // these keywords are used by all SQL dialects (however, a mode can still overwrite it) 259 | var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from having in insert into is join like not on or order select set table union update values where "; 260 | 261 | // turn a space-separated list into an array 262 | function set(str) { 263 | var obj = {}, words = str.split(" "); 264 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 265 | return obj; 266 | } 267 | 268 | // A generic SQL Mode. It's not a standard, it just try to support what is generally supported 269 | CodeMirror.defineMIME("text/x-sql", { 270 | name: "sql", 271 | keywords: set(sqlKeywords + "begin"), 272 | builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"), 273 | atoms: set("false true null unknown"), 274 | operatorChars: /^[*+\-%<>!=]/, 275 | dateSQL: set("date time timestamp"), 276 | support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") 277 | }); 278 | 279 | CodeMirror.defineMIME("text/x-mssql", { 280 | name: "sql", 281 | client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), 282 | keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered"), 283 | builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "), 284 | atoms: set("false true null unknown"), 285 | operatorChars: /^[*+\-%<>!=]/, 286 | dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"), 287 | hooks: { 288 | "@": hookVar 289 | } 290 | }); 291 | 292 | CodeMirror.defineMIME("text/x-mysql", { 293 | name: "sql", 294 | client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), 295 | keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group groupby_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), 296 | builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), 297 | atoms: set("false true null unknown"), 298 | operatorChars: /^[*+\-%<>!=&|^]/, 299 | dateSQL: set("date time timestamp"), 300 | support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), 301 | hooks: { 302 | "@": hookVar, 303 | "`": hookIdentifier, 304 | "\\": hookClient 305 | } 306 | }); 307 | 308 | CodeMirror.defineMIME("text/x-mariadb", { 309 | name: "sql", 310 | client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), 311 | keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), 312 | builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), 313 | atoms: set("false true null unknown"), 314 | operatorChars: /^[*+\-%<>!=&|^]/, 315 | dateSQL: set("date time timestamp"), 316 | support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), 317 | hooks: { 318 | "@": hookVar, 319 | "`": hookIdentifier, 320 | "\\": hookClient 321 | } 322 | }); 323 | 324 | // the query language used by Apache Cassandra is called CQL, but this mime type 325 | // is called Cassandra to avoid confusion with Contextual Query Language 326 | CodeMirror.defineMIME("text/x-cassandra", { 327 | name: "sql", 328 | client: { }, 329 | keywords: set("use select from using consistency where limit first reversed first and in insert into values using consistency ttl update set delete truncate begin batch apply create keyspace with columnfamily primary key index on drop alter type add any one quorum all local_quorum each_quorum"), 330 | builtin: set("ascii bigint blob boolean counter decimal double float int text timestamp uuid varchar varint"), 331 | atoms: set("false true"), 332 | operatorChars: /^[<>=]/, 333 | dateSQL: { }, 334 | support: set("commentSlashSlash decimallessFloat"), 335 | hooks: { } 336 | }); 337 | 338 | // this is based on Peter Raganitsch's 'plsql' mode 339 | CodeMirror.defineMIME("text/x-plsql", { 340 | name: "sql", 341 | client: set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"), 342 | keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"), 343 | builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"), 344 | operatorChars: /^[*+\-%<>!=~]/, 345 | dateSQL: set("date time timestamp"), 346 | support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber") 347 | }); 348 | 349 | // Created to support specific hive keywords 350 | CodeMirror.defineMIME("text/x-hive", { 351 | name: "sql", 352 | keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"), 353 | builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"), 354 | atoms: set("false true null unknown"), 355 | operatorChars: /^[*+\-%<>!=]/, 356 | dateSQL: set("date timestamp"), 357 | support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") 358 | }); 359 | }()); 360 | 361 | }); 362 | 363 | /* 364 | How Properties of Mime Types are used by SQL Mode 365 | ================================================= 366 | 367 | keywords: 368 | A list of keywords you want to be highlighted. 369 | functions: 370 | A list of function names you want to be highlighted. 371 | builtin: 372 | A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword"). 373 | operatorChars: 374 | All characters that must be handled as operators. 375 | client: 376 | Commands parsed and executed by the client (not the server). 377 | support: 378 | A list of supported syntaxes which are not common, but are supported by more than 1 DBMS. 379 | * ODBCdotTable: .tableName 380 | * zerolessFloat: .1 381 | * doubleQuote 382 | * nCharCast: N'string' 383 | * charsetCast: _utf8'string' 384 | * commentHash: use # char for comments 385 | * commentSlashSlash: use // for comments 386 | * commentSpaceRequired: require a space after -- for comments 387 | atoms: 388 | Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others: 389 | UNKNOWN, INFINITY, UNDERFLOW, NaN... 390 | dateSQL: 391 | Used for date/time SQL standard syntax, because not all DBMS's support same temporal types. 392 | */ 393 | -------------------------------------------------------------------------------- /app/public/js/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism-okaidia&languages=css+scss+sql */ 2 | self = (typeof window !== 'undefined') 3 | ? window // if in browser 4 | : ( 5 | (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) 6 | ? self // if in worker 7 | : {} // if in node js 8 | ); 9 | 10 | /** 11 | * Prism: Lightweight, robust, elegant syntax highlighting 12 | * MIT license http://www.opensource.org/licenses/mit-license.php/ 13 | * @author Lea Verou http://lea.verou.me 14 | */ 15 | 16 | var Prism = (function(){ 17 | 18 | // Private helper vars 19 | var lang = /\blang(?:uage)?-(?!\*)(\w+)\b/i; 20 | 21 | var _ = self.Prism = { 22 | util: { 23 | encode: function (tokens) { 24 | if (tokens instanceof Token) { 25 | return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias); 26 | } else if (_.util.type(tokens) === 'Array') { 27 | return tokens.map(_.util.encode); 28 | } else { 29 | return tokens.replace(/&/g, '&').replace(/ text.length) { 235 | // Something went terribly wrong, ABORT, ABORT! 236 | break tokenloop; 237 | } 238 | 239 | if (str instanceof Token) { 240 | continue; 241 | } 242 | 243 | pattern.lastIndex = 0; 244 | 245 | var match = pattern.exec(str); 246 | 247 | if (match) { 248 | if(lookbehind) { 249 | lookbehindLength = match[1].length; 250 | } 251 | 252 | var from = match.index - 1 + lookbehindLength, 253 | match = match[0].slice(lookbehindLength), 254 | len = match.length, 255 | to = from + len, 256 | before = str.slice(0, from + 1), 257 | after = str.slice(to + 1); 258 | 259 | var args = [i, 1]; 260 | 261 | if (before) { 262 | args.push(before); 263 | } 264 | 265 | var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias); 266 | 267 | args.push(wrapped); 268 | 269 | if (after) { 270 | args.push(after); 271 | } 272 | 273 | Array.prototype.splice.apply(strarr, args); 274 | } 275 | } 276 | } 277 | } 278 | 279 | return strarr; 280 | }, 281 | 282 | hooks: { 283 | all: {}, 284 | 285 | add: function (name, callback) { 286 | var hooks = _.hooks.all; 287 | 288 | hooks[name] = hooks[name] || []; 289 | 290 | hooks[name].push(callback); 291 | }, 292 | 293 | run: function (name, env) { 294 | var callbacks = _.hooks.all[name]; 295 | 296 | if (!callbacks || !callbacks.length) { 297 | return; 298 | } 299 | 300 | for (var i=0, callback; callback = callbacks[i++];) { 301 | callback(env); 302 | } 303 | } 304 | } 305 | }; 306 | 307 | var Token = _.Token = function(type, content, alias) { 308 | this.type = type; 309 | this.content = content; 310 | this.alias = alias; 311 | }; 312 | 313 | Token.stringify = function(o, language, parent) { 314 | if (typeof o == 'string') { 315 | return o; 316 | } 317 | 318 | if (Object.prototype.toString.call(o) == '[object Array]') { 319 | return o.map(function(element) { 320 | return Token.stringify(element, language, o); 321 | }).join(''); 322 | } 323 | 324 | var env = { 325 | type: o.type, 326 | content: Token.stringify(o.content, language, parent), 327 | tag: 'span', 328 | classes: ['token', o.type], 329 | attributes: {}, 330 | language: language, 331 | parent: parent 332 | }; 333 | 334 | if (env.type == 'comment') { 335 | env.attributes['spellcheck'] = 'true'; 336 | } 337 | 338 | if (o.alias) { 339 | var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias]; 340 | Array.prototype.push.apply(env.classes, aliases); 341 | } 342 | 343 | _.hooks.run('wrap', env); 344 | 345 | var attributes = ''; 346 | 347 | for (var name in env.attributes) { 348 | attributes += name + '="' + (env.attributes[name] || '') + '"'; 349 | } 350 | 351 | return '<' + env.tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + ''; 352 | 353 | }; 354 | 355 | if (!self.document) { 356 | if (!self.addEventListener) { 357 | // in Node.js 358 | return self.Prism; 359 | } 360 | // In worker 361 | self.addEventListener('message', function(evt) { 362 | var message = JSON.parse(evt.data), 363 | lang = message.language, 364 | code = message.code; 365 | 366 | self.postMessage(JSON.stringify(_.util.encode(_.tokenize(code, _.languages[lang])))); 367 | self.close(); 368 | }, false); 369 | 370 | return self.Prism; 371 | } 372 | 373 | // Get current script and highlight 374 | var script = document.getElementsByTagName('script'); 375 | 376 | script = script[script.length - 1]; 377 | 378 | if (script) { 379 | _.filename = script.src; 380 | 381 | if (document.addEventListener && !script.hasAttribute('data-manual')) { 382 | document.addEventListener('DOMContentLoaded', _.highlightAll); 383 | } 384 | } 385 | 386 | return self.Prism; 387 | 388 | })(); 389 | 390 | if (typeof module !== 'undefined' && module.exports) { 391 | module.exports = Prism; 392 | } 393 | ; 394 | Prism.languages.css = { 395 | 'comment': /\/\*[\w\W]*?\*\//g, 396 | 'atrule': { 397 | pattern: /@[\w-]+?.*?(;|(?=\s*{))/gi, 398 | inside: { 399 | 'punctuation': /[;:]/g 400 | } 401 | }, 402 | 'url': /url\((["']?).*?\1\)/gi, 403 | 'selector': /[^\{\}\s][^\{\};]*(?=\s*\{)/g, 404 | 'property': /(\b|\B)[\w-]+(?=\s*:)/ig, 405 | 'string': /("|')(\\?.)*?\1/g, 406 | 'important': /\B!important\b/gi, 407 | 'punctuation': /[\{\};:]/g, 408 | 'function': /[-a-z0-9]+(?=\()/ig 409 | }; 410 | 411 | if (Prism.languages.markup) { 412 | Prism.languages.insertBefore('markup', 'tag', { 413 | 'style': { 414 | pattern: /[\w\W]*?<\/style>/ig, 415 | inside: { 416 | 'tag': { 417 | pattern: /|<\/style>/ig, 418 | inside: Prism.languages.markup.tag.inside 419 | }, 420 | rest: Prism.languages.css 421 | } 422 | } 423 | }); 424 | }; 425 | Prism.languages.scss = Prism.languages.extend('css', { 426 | 'comment': { 427 | pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|\/\/.*?(\r?\n|$))/g, 428 | lookbehind: true 429 | }, 430 | // aturle is just the @***, not the entire rule (to highlight var & stuffs) 431 | // + add ability to highlight number & unit for media queries 432 | 'atrule': /@[\w-]+(?=\s+(\(|\{|;))/gi, 433 | // url, compassified 434 | 'url': /([-a-z]+-)*url(?=\()/gi, 435 | // CSS selector regex is not appropriate for Sass 436 | // since there can be lot more things (var, @ directive, nesting..) 437 | // a selector must start at the end of a property or after a brace (end of other rules or nesting) 438 | // it can contain some caracters that aren't used for defining rules or end of selector, & (parent selector), or interpolated variable 439 | // the end of a selector is found when there is no rules in it ( {} or {\s}) or if there is a property (because an interpolated var 440 | // can "pass" as a selector- e.g: proper#{$erty}) 441 | // this one was ard to do, so please be careful if you edit this one :) 442 | 'selector': /([^@;\{\}\(\)]?([^@;\{\}\(\)]|&|\#\{\$[-_\w]+\})+)(?=\s*\{(\}|\s|[^\}]+(:|\{)[^\}]+))/gm 443 | }); 444 | 445 | Prism.languages.insertBefore('scss', 'atrule', { 446 | 'keyword': /@(if|else if|else|for|each|while|import|extend|debug|warn|mixin|include|function|return|content)|(?=@for\s+\$[-_\w]+\s)+from/i 447 | }); 448 | 449 | Prism.languages.insertBefore('scss', 'property', { 450 | // var and interpolated vars 451 | 'variable': /((\$[-_\w]+)|(#\{\$[-_\w]+\}))/i 452 | }); 453 | 454 | Prism.languages.insertBefore('scss', 'ignore', { 455 | 'placeholder': /%[-_\w]+/i, 456 | 'statement': /\B!(default|optional)\b/gi, 457 | 'boolean': /\b(true|false)\b/g, 458 | 'null': /\b(null)\b/g, 459 | 'operator': /\s+([-+]{1,2}|={1,2}|!=|\|?\||\?|\*|\/|\%)\s+/g 460 | }); 461 | ; 462 | Prism.languages.cql= { 463 | 'comment': { 464 | pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|((--)|(\/\/)|#).*?(\r?\n|$))/g, 465 | lookbehind: true 466 | }, 467 | 'string' : /("|')(\\?[\s\S])*?\1/g, 468 | 'keyword' : /\b(ADD|ALL|ALTER|AND|ANY|APPLY|AS|ASC|AUTHORIZE|BATCH|BEGIN|BY|CLUSTERING|COLUMNFAMILY|COMPACT|CONSISTENCY|CONTAINS|COUNT|CREATE|CUSTOM|DELETE|DESC|DROP|DISTINCT|EXISTS|FROM|GRANT|IF|IN|INDEX|INSERT|INTO|KEY|KEYSPACE|LEVEL|LIMIT|MODIFY|NORECURSIVE|NOSUPERUSER|NOT|OF|ON|ORDER|PERMISSION|PERMISSIONS|PRIMARY|REVOKE|SCHEMA|SELECT|STATIC|STORAGE|SUPERUSER|TABLE|TOKEN|TRIGGER|TRUNCATE|TTL|TYPE|UPDATE|USE|USER|USERS|USING|VALUES|WHERE|WITH|WRITETIME|ASCII|BIGINT|BLOB|BOOLEAN|COUNTER|DECIMAL|DOUBLE|FLOAT|INET|INT|TEXT|TIMESTAMP|TIMEUUID|UUID|VARCHAR|VARINT|LIST|SET|MAP|NAN|INFINITY)\b/gi, 469 | 'boolean' : /\b(TRUE|FALSE|NULL)\b/gi, 470 | 'number' : /\b-?(0x)?\d*\.?[\da-f]+\b/g, 471 | 'operator' : /[-+]{1}|!|=?<|=?>|={1}|(&){1,2}|\|?\||\?|\*|\//gi, 472 | 'ignore' : /&(lt|gt|amp);/gi, 473 | 'punctuation' : /[;[\]{}:()`,.]/g 474 | }; 475 | ; 476 | -------------------------------------------------------------------------------- /app/public/js/prism.min.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism-okaidia&languages=sql */ 2 | self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){c.lastIndex=0;var m=c.exec(d);if(m){u&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),N=[p,1];b&&N.push(b);var O=new a(l,g?t.tokenize(m,g):m,h);N.push(O),w&&N.push(w),Array.prototype.splice.apply(r,N)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=s+'="'+(i.attributes[s]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; 3 | Prism.languages.sql={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|((--)|(\/\/)|#).*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?[\s\S])*?\1/g,keyword:/\b(ACTION|ADD|AFTER|ALGORITHM|ALTER|ANALYZE|APPLY|AS|ASC|AUTHORIZATION|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADE|CASCADED|CASE|CHAIN|CHAR VARYING|CHARACTER VARYING|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLUMN|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATA|DATABASE|DATABASES|DATETIME|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DOUBLE PRECISION|DROP|DUMMY|DUMP|DUMPFILE|DUPLICATE KEY|ELSE|ENABLE|ENCLOSED BY|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPE|ESCAPED BY|EXCEPT|EXEC|EXECUTE|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR|FOR EACH ROW|FORCE|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GEOMETRY|GEOMETRYCOLLECTION|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|IDENTITY|IDENTITY_INSERT|IDENTITYCOL|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTO|INVOKER|ISOLATION LEVEL|JOIN|KEY|KEYS|KILL|LANGUAGE SQL|LAST|LEFT|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONGBLOB|LONGTEXT|MATCH|MATCHED|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MERGE|MIDDLEINT|MODIFIES SQL DATA|MODIFY|MULTILINESTRING|MULTIPOINT|MULTIPOLYGON|NATIONAL|NATIONAL CHAR VARYING|NATIONAL CHARACTER|NATIONAL CHARACTER VARYING|NATIONAL VARCHAR|NATURAL|NCHAR|NCHAR VARCHAR|NEXT|NO|NO SQL|NOCHECK|NOCYCLE|NONCLUSTERED|NULLIF|NUMERIC|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPTIMIZE|OPTION|OPTIONALLY|ORDER|OUT|OUTER|OUTFILE|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREV|PRIMARY|PRINT|PRIVILEGES|PROC|PROCEDURE|PUBLIC|PURGE|QUICK|RAISERROR|READ|READS SQL DATA|READTEXT|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEATABLE|REPLICATION|REQUIRE|RESTORE|RESTRICT|RETURN|RETURNS|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROWCOUNT|ROWGUIDCOL|ROWS?|RTREE|RULE|SAVE|SAVEPOINT|SCHEMA|SELECT|SERIAL|SERIALIZABLE|SESSION|SESSION_USER|SET|SETUSER|SHARE MODE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|START|STARTING BY|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLE|TABLES|TABLESPACE|TEMPORARY|TEMPTABLE|TERMINATED BY|TEXT|TEXTSIZE|THEN|TIMESTAMP|TINYBLOB|TINYINT|TINYTEXT|TO|TOP|TRAN|TRANSACTION|TRANSACTIONS|TRIGGER|TRUNCATE|TSEQUAL|TYPE|TYPES|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNPIVOT|UPDATE|UPDATETEXT|USAGE|USE|USER|USING|VALUE|VALUES|VARBINARY|VARCHAR|VARCHARACTER|VARYING|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH|WITH ROLLUP|WITHIN|WORK|WRITE|WRITETEXT)\b/gi,"boolean":/\b(TRUE|FALSE|NULL)\b/gi,number:/\b-?(0x)?\d*\.?[\da-f]+\b/g,operator:/\b(ALL|AND|ANY|BETWEEN|EXISTS|IN|LIKE|NOT|OR|IS|UNIQUE|CHARACTER SET|COLLATE|DIV|OFFSET|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b|[-+]{1}|!|=?<|=?>|={1}|(&){1,2}|\|?\||\?|\*|\//gi,ignore:/&(lt|gt|amp);/gi,punctuation:/[;[\]()`,.]/g};; 4 | -------------------------------------------------------------------------------- /app/public/js/ui-codemirror.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Binds a CodeMirror widget to a 77 | 78 | 79 | --> 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /bin/cassandra-web: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'optparse' 4 | require 'logger' 5 | require 'thread' 6 | 7 | require 'resolv' 8 | require 'ipaddr' 9 | 10 | class CLI 11 | module Helpers 12 | def cluster 13 | settings.cluster 14 | end 15 | 16 | def session 17 | settings.session 18 | end 19 | end 20 | 21 | def initialize(env, out) 22 | @env = env 23 | @out = out 24 | @parser = OptionParser.new 25 | @options = { 26 | :bind => '0.0.0.0:3000', 27 | :log_level => 'info' 28 | } 29 | 30 | option(:bind, '-B', '--bind BIND', String, 'ip:port or path for cassandra web to bind on (default: 0.0.0.0:3000)') 31 | option(:hosts, '-H', '--hosts HOSTS', String, 'coma-separated list of cassandra hosts (default: 127.0.0.1)') 32 | option(:port, '-P', '--port PORT', Integer, 'integer port that cassandra is running on (default: 9042)') 33 | option(:log_level, '-L', '--log-level LEVEL', String, 'log level (default: info)') 34 | option(:username, '-u', '--username USER', String, 'username to use when connecting to cassandra') 35 | option(:password, '-p', '--password PASS', String, 'password to use when connecting to cassandra') 36 | option(:compression, '-C', '--compression NAME', String, 'compression algorithm to use (lz4 or snappy)') 37 | option(:server_cert, '--server-cert PATH', String, 'server ceritificate pathname') 38 | option(:client_cert, '--client-cert PATH', String, 'client ceritificate pathname') 39 | option(:private_key, '--private-key PATH', String, 'path to private key') 40 | option(:passphrase, '--passphrase SECRET', String, 'passphrase for the private key') 41 | 42 | @parser.on('-h', '--help', 'Show help') { show_help } 43 | end 44 | 45 | def run(argv) 46 | @parser.parse!(argv) 47 | 48 | require 'thin' 49 | require File.expand_path('../../', __FILE__) + '/app.rb' 50 | 51 | options = {} 52 | 53 | @options.each do |name, value| 54 | value = case name 55 | when :port, :username, :password 56 | value # return the value as is without modification 57 | when :hosts 58 | value.split(',').map!(&:strip) 59 | when :compression 60 | value.downcase.to_sym 61 | when :log_level 62 | name = :logger 63 | 64 | logger = Logger.new(@out) 65 | logger.level = Logger.const_get(value.upcase.to_sym) 66 | logger 67 | else 68 | next 69 | end 70 | 71 | options[name] = value 72 | end 73 | 74 | # add DNS resolution 75 | hosts = [] 76 | if options.empty? || Array(options[:hosts]).empty? 77 | hosts << '127.0.0.1' 78 | else 79 | Array(options[:hosts]).each do |host| 80 | case host 81 | when ::IPAddr 82 | hosts << host 83 | when ::String # ip address or hostname 84 | Resolv.each_address(host) do |ip| 85 | hosts << ::IPAddr.new(ip) 86 | end 87 | else 88 | raise ::ArgumentError, ":hosts must be String or IPAddr, #{host.inspect} given" 89 | end 90 | end 91 | end 92 | 93 | options[:load_balancing_policy] = ::Cassandra::LoadBalancing::Policies::WhiteList.new(hosts, ::Cassandra::LoadBalancing::Policies::RoundRobin.new) 94 | options[:compression] = :lz4 95 | options[:page_size] = nil 96 | 97 | cluster = ::Cassandra.cluster(options) 98 | 99 | App.set(:cluster, cluster) 100 | App.set(:session, cluster.connect) 101 | 102 | App.helpers Helpers 103 | 104 | Thin::Server.start(*@options[:bind].split(':'), App) 105 | rescue => e 106 | puts "#{e.class.name}: #{e.message}" 107 | puts "" 108 | show_help 109 | end 110 | 111 | private 112 | 113 | def option(name, *args) 114 | @parser.on(*args) {|v| @options[name] = v} 115 | end 116 | 117 | def show_help 118 | puts @parser 119 | exit 1 120 | end 121 | end 122 | 123 | CLI.new(ENV, $stderr).run(ARGV) 124 | -------------------------------------------------------------------------------- /cassandra-web.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | $: << File.expand_path('../lib', __FILE__) 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'cassandra-web' 7 | s.version = '0.5.0' 8 | s.authors = ['Bulat Shakirzyanov'] 9 | s.email = ['bulat.shakirzyanov@datastax.com'] 10 | s.homepage = 'https://github.com/avalanche123/cassandra-web' 11 | s.summary = %q{A simple web ui for Apache Cassandra} 12 | s.description = %q{Apache Cassandra web interface using Ruby, Event-machine, AngularJS, Server-Sent-Events and DataStax Ruby driver for Apache Cassandra} 13 | s.license = 'MIT' 14 | s.files = Dir['app/**/*.*', 'app.rb', 'README.md', 'bin/*'] 15 | s.bindir = 'bin' 16 | s.executables << 'cassandra-web' 17 | 18 | s.required_ruby_version = '>= 1.9.3' 19 | 20 | s.add_runtime_dependency 'cassandra-driver', '~> 3.1' 21 | s.add_runtime_dependency 'thin', '~> 1.6' 22 | s.add_runtime_dependency 'rack-cors', '>= 0.2', '< 2.0' 23 | s.add_runtime_dependency 'rack-parser', '~> 0.6' 24 | s.add_runtime_dependency 'sinatra', '~> 1.4' 25 | s.add_runtime_dependency 'lz4-ruby', '~> 0.3' 26 | 27 | s.add_development_dependency 'bundler', '~> 1.6' 28 | s.add_development_dependency 'rake', '~> 12.3' 29 | end 30 | --------------------------------------------------------------------------------