├── .gitignore ├── .travis.yml ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── Procfile ├── README.md ├── config.ru ├── contributing.md ├── deploy.sh ├── docker-compose.yml ├── front_end ├── ZeroClipboard.swf ├── app.rb ├── assets │ ├── css │ │ ├── _api.styl │ │ ├── _base.styl │ │ ├── _code_block.styl │ │ ├── _converter.styl │ │ ├── _fonts.styl │ │ ├── _footer.styl │ │ ├── _messages.styl │ │ ├── _mo.styl │ │ ├── _share.styl │ │ ├── _variables.styl │ │ ├── codemirror.css │ │ └── master.styl │ ├── favicon.ico │ ├── img │ │ ├── heart.png │ │ ├── heart@2x.png │ │ └── noise.png │ └── js │ │ ├── ZeroClipboard.min.js │ │ ├── codemirror.js │ │ ├── css.js │ │ ├── main.js │ │ ├── placeholder.js │ │ └── sass.js ├── fonts │ └── source-sans │ │ ├── sourcesanspro-bold-webfont.svg │ │ ├── sourcesanspro-bold-webfont.ttf │ │ ├── sourcesanspro-bold-webfont.woff │ │ ├── sourcesanspro-regular-webfont.eot │ │ ├── sourcesanspro-regular-webfont.svg │ │ ├── sourcesanspro-regular-webfont.ttf │ │ └── sourcesanspro-regular-webfont.woff └── views │ ├── _api.haml │ ├── _converter.haml │ ├── _footer.haml │ ├── _messages.haml │ ├── _share.haml │ ├── index.haml │ └── layout.haml ├── functions.yml ├── node_converter.rb ├── package-lock.json ├── package.json ├── sass2stylus.js ├── spec ├── fixtures │ ├── argument_splat.sass │ ├── argument_splat.scss │ ├── argument_splat.styl │ ├── atroot.sass │ ├── atroot.scss │ ├── atroot.styl │ ├── charset.scss │ ├── charset.styl │ ├── comments.scss │ ├── comments.styl │ ├── content.sass │ ├── content.scss │ ├── content.styl │ ├── debug.sass │ ├── debug.scss │ ├── debug.styl │ ├── disabled_functions.sass │ ├── disabled_functions.scss │ ├── disabled_functions.styl │ ├── each.sass │ ├── each.scss │ ├── each.styl │ ├── extend.sass │ ├── extend.scss │ ├── extend.styl │ ├── font_face.sass │ ├── font_face.scss │ ├── font_face.styl │ ├── foo.sass │ ├── foo.scss │ ├── foo.styl │ ├── for.sass │ ├── for.scss │ ├── for.styl │ ├── import.sass │ ├── import.scss │ ├── import.styl │ ├── interpolated_key_values.sass │ ├── interpolated_key_values.scss │ ├── interpolated_key_values.styl │ ├── interpolation.sass │ ├── interpolation.scss │ ├── interpolation.styl │ ├── media_queries.sass │ ├── media_queries.scss │ ├── media_queries.styl │ ├── multi_line_selectors.sass │ ├── multi_line_selectors.scss │ ├── multi_line_selectors.styl │ ├── nested_properties.sass │ ├── nested_properties.scss │ ├── nested_properties.styl │ ├── placeholder_selectors.sass │ ├── placeholder_selectors.scss │ ├── placeholder_selectors.styl │ ├── prop_negated_variables.sass │ ├── prop_negated_variables.scss │ ├── prop_negated_variables.styl │ ├── prop_operations.sass │ ├── prop_operations.scss │ ├── prop_operations.styl │ ├── warn.sass │ ├── warn.scss │ ├── warn.styl │ ├── while.sass │ ├── while.scss │ └── while.styl └── to_stylus_spec.rb └── to_stylus.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### Node ### 4 | lib-cov 5 | lcov.info 6 | *.seed 7 | *.log 8 | *.csv 9 | *.dat 10 | *.out 11 | *.pid 12 | *.gz 13 | 14 | pids 15 | logs 16 | results 17 | build 18 | .grunt 19 | 20 | node_modules 21 | 22 | 23 | ### SASS ### 24 | ### Sass Ignores - "Sassy CSS" http://sass-lang.com/ 25 | *.sass-cache 26 | 27 | 28 | ### Ruby ### 29 | *.gem 30 | *.rbc 31 | /.config 32 | /coverage/ 33 | /InstalledFiles 34 | /pkg/ 35 | /spec/reports/ 36 | /test/tmp/ 37 | /test/version_tmp/ 38 | /tmp/ 39 | 40 | ## Documentation cache and generated files: 41 | /.yardoc/ 42 | /_yardoc/ 43 | /doc/ 44 | /rdoc/ 45 | */public/* 46 | 47 | ## Environment normalisation: 48 | /.bundle/ 49 | /lib/bundler/man/ 50 | 51 | # for a library or gem, you might want to ignore these files since the code is 52 | # intended to run in multiple environments; otherwise, check them in: 53 | # Gemfile.lock 54 | .ruby-version 55 | # .ruby-gemset 56 | 57 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 58 | .rvmrc 59 | 60 | .DS_Store 61 | 62 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 2.0.0 3 | 4 | script: bundle exec rspec spec 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.4.9 2 | 3 | RUN curl -sL https://deb.nodesource.com/setup_10.x | bash 4 | RUN apt-get update -qq && apt-get install -yq build-essential nodejs 5 | 6 | RUN mkdir /app 7 | WORKDIR /app 8 | ADD . /app 9 | RUN bundle install 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby '2.4.9' 4 | gem 'sinatra' 5 | gem 'sass', '3.3.2' 6 | gem 'haml' 7 | 8 | group :development, :test do 9 | gem 'rspec' 10 | gem 'foreman' 11 | end 12 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | diff-lcs (1.2.5) 5 | foreman (0.85.0) 6 | thor (~> 0.19.1) 7 | haml (5.1.2) 8 | temple (>= 0.8.0) 9 | tilt 10 | rack (1.6.11) 11 | rack-protection (1.5.5) 12 | rack 13 | rspec (2.14.1) 14 | rspec-core (~> 2.14.0) 15 | rspec-expectations (~> 2.14.0) 16 | rspec-mocks (~> 2.14.0) 17 | rspec-core (2.14.8) 18 | rspec-expectations (2.14.5) 19 | diff-lcs (>= 1.1.3, < 2.0) 20 | rspec-mocks (2.14.6) 21 | sass (3.3.2) 22 | sinatra (1.4.4) 23 | rack (~> 1.4) 24 | rack-protection (~> 1.4) 25 | tilt (~> 1.3, >= 1.3.4) 26 | temple (0.8.1) 27 | thor (0.19.4) 28 | tilt (1.4.1) 29 | 30 | PLATFORMS 31 | ruby 32 | 33 | DEPENDENCIES 34 | foreman 35 | haml 36 | rspec 37 | sass (= 3.3.2) 38 | sinatra 39 | 40 | BUNDLED WITH 41 | 1.11.2 42 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | front_end: bundle exec rackup config.ru -p 3838 -o '0.0.0.0' 2 | assets: npm run watch 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sass2Stylus 2 | ---------- 3 | #[Use it live](http://sass2stylus.com) 4 | 5 | [![Build Status](https://travis-ci.org/mojotech/sass2stylus.svg)](https://travis-ci.org/mojotech/sass2stylus) 6 | 7 | ### API Usage 8 | 9 | #### With cURL 10 | ```sh 11 | curl -F file=@/your/local/file.scss http://sass2stylus.com/api > new_file.styl 12 | ``` 13 | #### With Ruby 14 | ```ruby 15 | RestClient.post('http://sass2stylus.com/api', file: File.open('local_file.scss')) 16 | ``` 17 | --- 18 | 19 | ##### Sass2Stylus is curated by loving hands at... 20 | (psst, [we're hiring](http://www.mojotech.com/jobs)) 21 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | 4 | Bundler.require 5 | 6 | set :root, File.dirname(__FILE__) 7 | set :views, File.expand_path("front_end/views", settings.root) 8 | set :public_folder, File.expand_path("front_end/public", settings.root) 9 | require File.expand_path("front_end/app.rb", settings.root) 10 | run Sinatra::Application 11 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | Sass2Stylus 2 | ---------- 3 | #[Use it live](http://sass2stylus.com) 4 | 5 | ### Development 6 | - Clone this repository 7 | - `bundle install` 8 | - `npm install` 9 | - `npm run watch` 10 | - `rackup config.ru` 11 | 12 | #### With Docker 13 | - `docker-compose build` 14 | - `docker-compose up` 15 | - Navigate to http://localhost:3838 16 | 17 | ### Deployment to Heroku 18 | - `heroku create` 19 | - `./deploy.sh` 20 | 21 | --- 22 | 23 | ##### Sass2Stylus is curated by loving hands at... 24 | (psst, [we're hiring](http://www.mojotech.com/jobs)) 25 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | sed -i.bak 's/\*\/public\/\*//' .gitignore 2 | rm .gitignore.bak 3 | npm run build 4 | git checkout -b deploy 5 | git add front_end 6 | git commit -m 'deploy' 7 | git push -f heroku deploy:master 8 | git checkout master 9 | git branch -D deploy 10 | git reset --hard 11 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | app: 2 | build: . 3 | command: bash -lc "bundle install && npm install && bundle exec foreman start" 4 | restart: unless-stopped 5 | tty: true 6 | stdin_open: true 7 | volumes: 8 | - .:/app 9 | ports: 10 | - "3838:3838" 11 | -------------------------------------------------------------------------------- /front_end/ZeroClipboard.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/ZeroClipboard.swf -------------------------------------------------------------------------------- /front_end/app.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | require "haml" 3 | 4 | get '/' do 5 | haml :index 6 | end 7 | 8 | post '/ajax' do 9 | load File.expand_path('to_stylus.rb', settings.root) 10 | options = {syntax: params[:sass_textarea].include?(";") ? :scss : :sass} 11 | engine = Sass::Engine.new( params[:sass_textarea], options ) 12 | begin 13 | tree = engine.to_tree 14 | stylus = ToStylus.visit(tree) 15 | rescue => e 16 | end 17 | e.nil? ? stylus : "Error: #{e.message} \nLine: #{e.sass_line}" 18 | end 19 | 20 | post '/download' do 21 | stylus_file = Tempfile.new("") 22 | stylus_file.write params[:stylus_textarea] 23 | stylus_file.rewind 24 | send_file(stylus_file, :filename => "new_file.styl") 25 | stylus_file.close! 26 | end 27 | 28 | post '/api' do 29 | load File.expand_path('to_stylus.rb', settings.root) 30 | options = Sass::Engine::DEFAULT_OPTIONS.merge({syntax: :scss}) 31 | engine = Sass::Engine.for_file(params[:file][:tempfile], options) 32 | tree = engine.to_tree 33 | stylus = ToStylus.visit(tree) 34 | 35 | stylus_file = Tempfile.new('new_file.stylus') 36 | stylus_file.write stylus 37 | stylus_file.rewind 38 | send_file stylus_file 39 | stylus_file.close! 40 | end 41 | -------------------------------------------------------------------------------- /front_end/assets/css/_api.styl: -------------------------------------------------------------------------------- 1 | .api-header 2 | padding-left med-padding-block 3 | padding-top med-padding-block 4 | margin-top 0 5 | .api 6 | background-color darken(light-blue, 5%) 7 | padding-top 10px 8 | .col-half 9 | padding 0 med-padding-block med-padding-block med-padding-block 10 | .api-code-block 11 | min-height inherit 12 | padding 10px 13 | font-size 12px 14 | white-space nowrap 15 | overflow-x scroll 16 | border-radius 0 17 | display inline-block 18 | width 78% 19 | height 28px 20 | vertical-align top 21 | line-height 10px 22 | .api-code-block::-webkit-scrollbar 23 | display none 24 | 25 | -------------------------------------------------------------------------------- /front_end/assets/css/_base.styl: -------------------------------------------------------------------------------- 1 | * 2 | box-sizing border-box 3 | html, body 4 | height 100% 5 | 6 | html 7 | &.no_flash .no_flash 8 | width: 100% 9 | 10 | &.no_flash #download_btn 11 | width: 100% 12 | 13 | &.no_flash .flash_btn 14 | display none 15 | 16 | #content 17 | position: relative 18 | min-height: 100% 19 | padding-bottom: 323px 20 | background-color: #f2f6f9 21 | min-width 768px 22 | 23 | body 24 | margin 0 25 | padding 0 26 | font-family source-sans 27 | font-weight normal 28 | 29 | .base-content 30 | position absolute 31 | bottom 0 32 | width 100% 33 | 34 | h1, h2, h3, .btn 35 | font-family source-sans-bold 36 | font-weight normal 37 | h3 38 | margin 0 0 10px 0 39 | a 40 | color blue 41 | &:hover 42 | color lighten(blue, 20%) 43 | 44 | p 45 | margin 0 46 | code 47 | display block 48 | background light-blue 49 | padding med-padding-block 50 | min-height 350px 51 | 52 | strong 53 | font-family source-sans-bold 54 | 55 | .contain 56 | width 60% 57 | margin 0 auto 58 | 59 | .inline-link 60 | border-bottom 1px dotted blue 61 | transition color 100ms ease-out, border-bottom 100ms ease-out 62 | text-decoration none 63 | color blue 64 | &:hover 65 | text-decoration none 66 | color darken(blue, 50%) 67 | border-bottom 1px dotted darken(blue, 50%) 68 | .btn 69 | color white 70 | font-size 16px 71 | text-decoration none 72 | text-transform uppercase 73 | background green 74 | padding (small-padding-block - 7) small-padding-block 75 | outline: none 76 | border: none 77 | margin 0 78 | &:hover 79 | color white 80 | text-decoration none 81 | background-color lighten(green, 10%) 82 | cursor pointer 83 | &:active 84 | background-color darken(green, 20%) 85 | &.btn-stretch-row 86 | width 50% 87 | float left 88 | padding 15.5px 0 89 | 90 | .zeroclipboard-is-hover 91 | background-color #97d978 92 | .zeroclipboard-is-active 93 | position relative 94 | top 2px 95 | 96 | .col-header 97 | display inline-block 98 | width 50% 99 | background-color dark-blue 100 | margin 0 101 | padding small-padding-block 0 small-padding-block (med-padding-block + 14) 102 | color white 103 | 104 | .clear 105 | clear both 106 | 107 | form 108 | height 100% 109 | 110 | .col-half 111 | width 50% 112 | float left 113 | &.converter-col 114 | position relative 115 | 116 | .inner-padding 117 | height 100% 118 | 119 | .action-buttons 120 | display inline-block 121 | position absolute 122 | top 0 123 | right 0 124 | width: 50% 125 | -------------------------------------------------------------------------------- /front_end/assets/css/_code_block.styl: -------------------------------------------------------------------------------- 1 | .CodeMirror, 2 | .cm-s-neat, 3 | .cm-s-default.CodeMirror 4 | background-color transparent 5 | 6 | .cm-s-default.CodeMirror 7 | border-right 1px solid darken(light-blue, 5%) 8 | 9 | 10 | .CodeMirror-gutters 11 | background-color lighten(light-blue, 25%) 12 | border none 13 | 14 | .cm-s-default.CodeMirror, 15 | .cm-s-neat 16 | border-radius 0 17 | 18 | .CodeMirror 19 | height 100% 20 | 21 | .CodeMirror pre 22 | font-family Monaco, monospace 23 | font-size 12px 24 | 25 | .sass-col, .styl-col 26 | background-color lighten(light-blue, 25%) 27 | 28 | .cm-s-default .cm-qualifier, 29 | .cm-qualifier 30 | color red 31 | 32 | .cm-s-default .cm-property, 33 | .cm-property, 34 | .cm-s-default .cm-tag 35 | color #7B7E9B 36 | 37 | .cm-s-default .cm-def, 38 | .cm-def 39 | color #8B8CA0 40 | 41 | .CodeMirror-linenumber 42 | color #C8D3E4 43 | -------------------------------------------------------------------------------- /front_end/assets/css/_converter.styl: -------------------------------------------------------------------------------- 1 | .converter 2 | padding xl-padding-block 3 | .btn 4 | margin-bottom small-padding-block + 7 5 | #file_selector 6 | display none 7 | -------------------------------------------------------------------------------- /front_end/assets/css/_fonts.styl: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family 'Source Sans Regular' 3 | src url('../fonts/source-sans/sourcesanspro-regular-webfont.eot') 4 | src url('../fonts/source-sans/sourcesanspro-regular-webfont.eot?#iefix') format('embedded-opentype'), 5 | url('../fonts/source-sans/sourcesanspro-regular-webfont.woff') format('woff'), 6 | url('../fonts/source-sans/sourcesanspro-regular-webfont.ttf') format('truetype'), 7 | url('../fonts/source-sans/sourcesanspro-regular-webfont.svg#webfont') format('svg'); 8 | @font-face 9 | font-family 'Source Sans Bold' 10 | src url('../fonts/source-sans/sourcesanspro-bold-webfont.eot') 11 | src url('../fonts/source-sans/sourcesanspro-bold-webfont.eot?#iefix') format('embedded-opentype'), 12 | url('../fonts/source-sans/sourcesanspro-bold-webfont.woff') format('woff'), 13 | url('../fonts/source-sans/sourcesanspro-bold-webfont.ttf') format('truetype'), 14 | url('../fonts/source-sans/sourcesanspro-bold-webfont.svg#webfont') format('svg'); 15 | -------------------------------------------------------------------------------- /front_end/assets/css/_footer.styl: -------------------------------------------------------------------------------- 1 | .footer 2 | text-align center 3 | padding med-padding-block 0 4 | color light-blue 5 | background dark-grey 6 | font-size 13px 7 | #gh_btn 8 | position absolute 9 | right 10px 10 | bottom 22px 11 | 12 | -------------------------------------------------------------------------------- /front_end/assets/css/_messages.styl: -------------------------------------------------------------------------------- 1 | .messages 2 | display none 3 | padding small-padding-block 0 4 | &.error 5 | background red 6 | .active-message 7 | display block 8 | .close 9 | margin-top -10px 10 | padding 7px 15px 14px 14px 11 | opacity 1 12 | transition all 500ms ease-out 13 | &:hover 14 | color white 15 | opacity 1 16 | transform rotateZ(180deg) 17 | -------------------------------------------------------------------------------- /front_end/assets/css/_mo.styl: -------------------------------------------------------------------------------- 1 | pos(args...) 2 | positions = absolute, fixed, relative, static, inherit, initial 3 | props = top, right, bottom, left 4 | 5 | if args[0] in positions 6 | position: args[0] 7 | args = args[1..length(args)] 8 | 9 | if args[0] is 'fill' 10 | args = args[1..length(args)] 11 | for prop, i in props 12 | {prop}: args[i] or 0 unless args[i] is false 13 | else 14 | for arg, i in args 15 | {props[i]}: arg unless arg is false 16 | 17 | ellipsis(width = false) 18 | width: width unless width is false 19 | overflow: hidden 20 | white-space: nowrap 21 | text-overflow: ellipsis 22 | 23 | size(width, height = width) 24 | width: width 25 | height: height 26 | 27 | retina-background-image(name, extension = png) 28 | background-image url(name'.'extension) 29 | @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) 30 | background-image url(name'@2x.'extension) 31 | -------------------------------------------------------------------------------- /front_end/assets/css/_share.styl: -------------------------------------------------------------------------------- 1 | .share 2 | margin med-padding-block 0 3 | 4 | .social-network-link 5 | &.mt-share-inline-square-sm 6 | display inline-block 7 | width 42px 8 | text-align center 9 | img 10 | width 34px 11 | height auto 12 | border 0px 13 | 14 | .mt-google 15 | background-color rgb(221, 75, 57) 16 | &:hover 17 | background-color rgb(225, 95, 79) 18 | 19 | .mt-linkedin 20 | background-color rgb(14, 118, 168) 21 | &:hover 22 | background-color rgb(16, 135, 192) 23 | 24 | .mt-twitter 25 | background-color rgb(0, 172, 238) 26 | &:hover 27 | background-color rgb(8, 187, 255) 28 | 29 | .mt-facebook 30 | background-color rgb(59, 89, 152) 31 | &:hover 32 | background-color rgb(66, 100, 170) 33 | 34 | .mt-pinterest 35 | background-color rgb(204, 33, 39) 36 | &:hover 37 | background-color rgb(221, 42, 48) 38 | 39 | -------------------------------------------------------------------------------- /front_end/assets/css/_variables.styl: -------------------------------------------------------------------------------- 1 | light-blue = #eef3f7 2 | blue = #00a6ea 3 | dark-blue = #333c4e 4 | green = #7dd056 5 | grey = #b1bfd4 6 | dark-grey = #333333 7 | red = #ed1c37 8 | 9 | med-padding-block = 26px 10 | small-padding-block = 10px 11 | 12 | source-sans = "Source Sans Regular", Helvetica, Helvetica, Arial, sans-serif 13 | source-sans-bold = "Source Sans Bold", Helvetica, Arial, sans-serif 14 | -------------------------------------------------------------------------------- /front_end/assets/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 | color: black; 8 | } 9 | .CodeMirror-scroll { 10 | /* Set scrolling behaviour here */ 11 | overflow: auto; 12 | } 13 | .CodeMirror-code{ 14 | min-height: 300px; 15 | } 16 | 17 | /* PADDING */ 18 | 19 | .CodeMirror-lines { 20 | padding: 4px 0; /* Vertical padding around content */ 21 | } 22 | .CodeMirror pre { 23 | padding: 0 4px; /* Horizontal padding of content */ 24 | } 25 | 26 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 27 | background-color: white; /* The little square between H and V scrollbars */ 28 | } 29 | 30 | /* GUTTER */ 31 | 32 | .CodeMirror-gutters { 33 | border-right: 1px solid #ddd; 34 | background-color: #E9F0FA; 35 | white-space: nowrap; 36 | } 37 | .CodeMirror-linenumbers {} 38 | .CodeMirror-linenumber { 39 | padding: 0 3px 0 5px; 40 | min-width: 20px; 41 | text-align: right; 42 | color: #999; 43 | -moz-box-sizing: content-box; 44 | box-sizing: content-box; 45 | } 46 | 47 | /* CURSOR */ 48 | 49 | .CodeMirror div.CodeMirror-cursor { 50 | border-left: 1px solid black; 51 | z-index: 3; 52 | } 53 | /* Shown when moving in bi-directional text */ 54 | .CodeMirror div.CodeMirror-secondarycursor { 55 | border-left: 1px solid silver; 56 | } 57 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { 58 | width: auto; 59 | border: 0; 60 | background: #7e7; 61 | z-index: 1; 62 | } 63 | /* Can style cursor different in overwrite (non-insert) mode */ 64 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} 65 | 66 | .cm-tab { display: inline-block; } 67 | 68 | .CodeMirror-ruler { 69 | border-left: 1px solid #ccc; 70 | position: absolute; 71 | } 72 | 73 | /* DEFAULT THEME */ 74 | .cm-s-default.CodeMirror { 75 | background: #E9F0FA; 76 | color: #F8F8F8; 77 | -webkit-border-radius: 6px; 78 | -moz-border-radius: 6px; 79 | -o-border-radius: 6px; 80 | -ms-border-radius: 6px; 81 | border-radius: 6px; 82 | } 83 | .cm-s-default .cm-keyword {color: #708;} 84 | .cm-s-default .cm-atom {color: #219;} 85 | .cm-s-default .cm-number {color: #3a3;} 86 | .cm-s-default .cm-def {color: #00f;} 87 | .cm-s-default .cm-variable {color: black;} 88 | .cm-s-default .cm-variable-2 {color: #05a;} 89 | .cm-s-default .cm-variable-3 {color: #3a3;} 90 | .cm-s-default .cm-property {color: black;} 91 | .cm-s-default .cm-operator {color: black;} 92 | .cm-s-default .cm-comment {color: #a50;} 93 | .cm-s-default .cm-string {color: #a11;} 94 | .cm-s-default .cm-string-2 {color: #f50;} 95 | .cm-s-default .cm-meta {color: #555;} 96 | .cm-s-default .cm-qualifier {color: #555;} 97 | .cm-s-default .cm-builtin {color: #3a3;} 98 | .cm-s-default .cm-bracket {color: #997;} 99 | .cm-s-default .cm-tag {color: #000;} 100 | .cm-s-default .cm-attribute {color: #00c;} 101 | .cm-s-default .cm-header {color: blue;} 102 | .cm-s-default .cm-quote {color: #090;} 103 | .cm-s-default .cm-hr {color: #999;} 104 | .cm-s-default .cm-link {color: #00c;} 105 | 106 | .cm-negative {color: #d44;} 107 | .cm-positive {color: #292;} 108 | .cm-header, .cm-strong {font-weight: bold;} 109 | .cm-em {font-style: italic;} 110 | .cm-link {text-decoration: underline;} 111 | 112 | .cm-s-default .cm-error {color: #f00;} 113 | .cm-invalidchar {color: #f00;} 114 | 115 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 116 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 117 | .CodeMirror-activeline-background {background: #e8f2ff;} 118 | 119 | /* STOP */ 120 | 121 | /* The rest of this file contains styles related to the mechanics of 122 | the editor. You probably shouldn't touch them. */ 123 | 124 | .CodeMirror { 125 | line-height: 1.2; 126 | position: relative; 127 | overflow: hidden; 128 | background: white; 129 | color: black; 130 | } 131 | 132 | .CodeMirror-scroll { 133 | /* 30px is the magic margin used to hide the element's real scrollbars */ 134 | /* See overflow: hidden in .CodeMirror */ 135 | margin-bottom: -30px; margin-right: -30px; 136 | padding-bottom: 30px; 137 | height: 100%; 138 | outline: none; /* Prevent dragging from highlighting the element */ 139 | position: relative; 140 | -moz-box-sizing: content-box; 141 | box-sizing: content-box; 142 | } 143 | .CodeMirror-sizer { 144 | position: relative; 145 | border-right: 30px solid transparent; 146 | -moz-box-sizing: content-box; 147 | box-sizing: content-box; 148 | } 149 | 150 | /* The fake, visible scrollbars. Used to force redraw during scrolling 151 | before actuall scrolling happens, thus preventing shaking and 152 | flickering artifacts. */ 153 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 154 | position: absolute; 155 | z-index: 6; 156 | display: none; 157 | } 158 | .CodeMirror-vscrollbar { 159 | right: 0; top: 0; 160 | overflow-x: hidden; 161 | overflow-y: scroll; 162 | } 163 | .CodeMirror-hscrollbar { 164 | bottom: 0; left: 0; 165 | overflow-y: hidden; 166 | overflow-x: scroll; 167 | } 168 | .CodeMirror-scrollbar-filler { 169 | right: 0; bottom: 0; 170 | } 171 | .CodeMirror-gutter-filler { 172 | left: 0; bottom: 0; 173 | } 174 | 175 | .CodeMirror-gutters { 176 | position: absolute; left: 0; top: 0; 177 | padding-bottom: 30px; 178 | z-index: 3; 179 | } 180 | .CodeMirror-gutter { 181 | white-space: normal; 182 | height: 100%; 183 | -moz-box-sizing: content-box; 184 | box-sizing: content-box; 185 | padding-bottom: 30px; 186 | margin-bottom: -32px; 187 | display: inline-block; 188 | /* Hack to make IE7 behave */ 189 | *zoom:1; 190 | *display:inline; 191 | } 192 | .CodeMirror-gutter-elt { 193 | position: absolute; 194 | cursor: default; 195 | z-index: 4; 196 | } 197 | 198 | .CodeMirror-lines { 199 | cursor: text; 200 | } 201 | .CodeMirror pre { 202 | /* Reset some styles that the rest of the page might have set */ 203 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 204 | border-width: 0; 205 | background: transparent; 206 | font-family: inherit; 207 | font-size: inherit; 208 | margin: 0; 209 | white-space: pre; 210 | word-wrap: normal; 211 | line-height: inherit; 212 | color: black; 213 | z-index: 2; 214 | position: relative; 215 | overflow: visible; 216 | } 217 | .CodeMirror-wrap pre { 218 | word-wrap: break-word; 219 | white-space: pre-wrap; 220 | word-break: normal; 221 | } 222 | 223 | .CodeMirror-linebackground { 224 | position: absolute; 225 | left: 0; right: 0; top: 0; bottom: 0; 226 | z-index: 0; 227 | } 228 | 229 | .CodeMirror-linewidget { 230 | position: relative; 231 | z-index: 2; 232 | overflow: auto; 233 | } 234 | 235 | .CodeMirror-widget {} 236 | 237 | .CodeMirror-wrap .CodeMirror-scroll { 238 | overflow-x: hidden; 239 | } 240 | 241 | .CodeMirror-measure { 242 | position: absolute; 243 | width: 100%; 244 | height: 0; 245 | overflow: hidden; 246 | visibility: hidden; 247 | } 248 | .CodeMirror-measure pre { position: static; } 249 | 250 | .CodeMirror div.CodeMirror-cursor { 251 | position: absolute; 252 | visibility: hidden; 253 | border-right: none; 254 | width: 0; 255 | } 256 | .CodeMirror-focused div.CodeMirror-cursor { 257 | visibility: visible; 258 | } 259 | 260 | .CodeMirror-selected { background: #d9d9d9; } 261 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 262 | 263 | .cm-searching { 264 | background: #ffa; 265 | background: rgba(255, 255, 0, .4); 266 | } 267 | 268 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 269 | .CodeMirror span { *vertical-align: text-bottom; } 270 | 271 | @media print { 272 | /* Hide the cursor when printing */ 273 | .CodeMirror div.CodeMirror-cursor { 274 | visibility: hidden; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /front_end/assets/css/master.styl: -------------------------------------------------------------------------------- 1 | reset() 2 | base() 3 | 4 | @import '_mo' 5 | @import '_fonts' 6 | @import '_variables' 7 | @import '_base' 8 | @import '_converter' 9 | @import '_code_block' 10 | @import '_messages' 11 | @import '_api' 12 | @import '_footer' 13 | @import '_share' 14 | -------------------------------------------------------------------------------- /front_end/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/assets/favicon.ico -------------------------------------------------------------------------------- /front_end/assets/img/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/assets/img/heart.png -------------------------------------------------------------------------------- /front_end/assets/img/heart@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/assets/img/heart@2x.png -------------------------------------------------------------------------------- /front_end/assets/img/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/assets/img/noise.png -------------------------------------------------------------------------------- /front_end/assets/js/ZeroClipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ZeroClipboard 3 | * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface. 4 | * Copyright (c) 2014 Jon Rohan, James M. Greene 5 | * Licensed MIT 6 | * http://zeroclipboard.org/ 7 | * v1.3.2 8 | */ 9 | !function(){"use strict";function a(a){return a.replace(/,/g,".").replace(/[^0-9\.]/g,"")}function b(b){return parseFloat(a(b))>=10}var c,d={bridge:null,version:"0.0.0",disabled:null,outdated:null,ready:null},e={},f=0,g={},h=0,i={},j=null,k=null,l=function(){var a,b,c,d,e="ZeroClipboard.swf";if(document.currentScript&&(d=document.currentScript.src));else{var f=document.getElementsByTagName("script");if("readyState"in f[0])for(a=f.length;a--&&("interactive"!==f[a].readyState||!(d=f[a].src)););else if("loading"===document.readyState)d=f[f.length-1].src;else{for(a=f.length;a--;){if(c=f[a].src,!c){b=null;break}if(c=c.split("#")[0].split("?")[0],c=c.slice(0,c.lastIndexOf("/")+1),null==b)b=c;else if(b!==c){b=null;break}}null!==b&&(d=b)}}return d&&(d=d.split("#")[0].split("?")[0],e=d.slice(0,d.lastIndexOf("/")+1)+e),e}(),m=function(){var a=/\-([a-z])/g,b=function(a,b){return b.toUpperCase()};return function(c){return c.replace(a,b)}}(),n=function(a,b){var c,d,e;return window.getComputedStyle?c=window.getComputedStyle(a,null).getPropertyValue(b):(d=m(b),c=a.currentStyle?a.currentStyle[d]:a.style[d]),"cursor"!==b||c&&"auto"!==c||(e=a.tagName.toLowerCase(),"a"!==e)?c:"pointer"},o=function(a){a||(a=window.event);var b;this!==window?b=this:a.target?b=a.target:a.srcElement&&(b=a.srcElement),I.activate(b)},p=function(a,b,c){a&&1===a.nodeType&&(a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,c))},q=function(a,b,c){a&&1===a.nodeType&&(a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent&&a.detachEvent("on"+b,c))},r=function(a,b){if(!a||1!==a.nodeType)return a;if(a.classList)return a.classList.contains(b)||a.classList.add(b),a;if(b&&"string"==typeof b){var c=(b||"").split(/\s+/);if(1===a.nodeType)if(a.className){for(var d=" "+a.className+" ",e=a.className,f=0,g=c.length;g>f;f++)d.indexOf(" "+c[f]+" ")<0&&(e+=" "+c[f]);a.className=e.replace(/^\s+|\s+$/g,"")}else a.className=b}return a},s=function(a,b){if(!a||1!==a.nodeType)return a;if(a.classList)return a.classList.contains(b)&&a.classList.remove(b),a;if(b&&"string"==typeof b||void 0===b){var c=(b||"").split(/\s+/);if(1===a.nodeType&&a.className)if(b){for(var d=(" "+a.className+" ").replace(/[\n\t]/g," "),e=0,f=c.length;f>e;e++)d=d.replace(" "+c[e]+" "," ");a.className=d.replace(/^\s+|\s+$/g,"")}else a.className=""}return a},t=function(){var a,b,c,d=1;return"function"==typeof document.body.getBoundingClientRect&&(a=document.body.getBoundingClientRect(),b=a.right-a.left,c=document.body.offsetWidth,d=Math.round(b/c*100)/100),d},u=function(a,b){var c={left:0,top:0,width:0,height:0,zIndex:A(b)-1};if(a.getBoundingClientRect){var d,e,f,g=a.getBoundingClientRect();"pageXOffset"in window&&"pageYOffset"in window?(d=window.pageXOffset,e=window.pageYOffset):(f=t(),d=Math.round(document.documentElement.scrollLeft/f),e=Math.round(document.documentElement.scrollTop/f));var h=document.documentElement.clientLeft||0,i=document.documentElement.clientTop||0;c.left=g.left+d-h,c.top=g.top+e-i,c.width="width"in g?g.width:g.right-g.left,c.height="height"in g?g.height:g.bottom-g.top}return c},v=function(a,b){var c=null==b||b&&b.cacheBust===!0&&b.useNoCache===!0;return c?(-1===a.indexOf("?")?"?":"&")+"noCache="+(new Date).getTime():""},w=function(a){var b,c,d,e=[],f=[],g=[];if(a.trustedOrigins&&("string"==typeof a.trustedOrigins?f.push(a.trustedOrigins):"object"==typeof a.trustedOrigins&&"length"in a.trustedOrigins&&(f=f.concat(a.trustedOrigins))),a.trustedDomains&&("string"==typeof a.trustedDomains?f.push(a.trustedDomains):"object"==typeof a.trustedDomains&&"length"in a.trustedDomains&&(f=f.concat(a.trustedDomains))),f.length)for(b=0,c=f.length;c>b;b++)if(f.hasOwnProperty(b)&&f[b]&&"string"==typeof f[b]){if(d=D(f[b]),!d)continue;if("*"===d){g=[d];break}g.push.apply(g,[d,"//"+d,window.location.protocol+"//"+d])}return g.length&&e.push("trustedOrigins="+encodeURIComponent(g.join(","))),"string"==typeof a.jsModuleId&&a.jsModuleId&&e.push("jsModuleId="+encodeURIComponent(a.jsModuleId)),e.join("&")},x=function(a,b,c){if("function"==typeof b.indexOf)return b.indexOf(a,c);var d,e=b.length;for("undefined"==typeof c?c=0:0>c&&(c=e+c),d=c;e>d;d++)if(b.hasOwnProperty(d)&&b[d]===a)return d;return-1},y=function(a){if("string"==typeof a)throw new TypeError("ZeroClipboard doesn't accept query strings.");return a.length?a:[a]},z=function(a,b,c,d){d?window.setTimeout(function(){a.apply(b,c)},0):a.apply(b,c)},A=function(a){var b,c;return a&&("number"==typeof a&&a>0?b=a:"string"==typeof a&&(c=parseInt(a,10))&&!isNaN(c)&&c>0&&(b=c)),b||("number"==typeof L.zIndex&&L.zIndex>0?b=L.zIndex:"string"==typeof L.zIndex&&(c=parseInt(L.zIndex,10))&&!isNaN(c)&&c>0&&(b=c)),b||0},B=function(a,b){if(a&&b!==!1&&"undefined"!=typeof console&&console&&(console.warn||console.log)){var c="`"+a+"` is deprecated. See docs for more info:\n https://github.com/zeroclipboard/zeroclipboard/blob/master/docs/instructions.md#deprecations";console.warn?console.warn(c):console.log(c)}},C=function(){var a,b,c,d,e,f,g=arguments[0]||{};for(a=1,b=arguments.length;b>a;a++)if(null!=(c=arguments[a]))for(d in c)if(c.hasOwnProperty(d)){if(e=g[d],f=c[d],g===f)continue;void 0!==f&&(g[d]=f)}return g},D=function(a){if(null==a||""===a)return null;if(a=a.replace(/^\s+|\s+$/g,""),""===a)return null;var b=a.indexOf("//");a=-1===b?a:a.slice(b+2);var c=a.indexOf("/");return a=-1===c?a:-1===b||0===c?null:a.slice(0,c),a&&".swf"===a.slice(-4).toLowerCase()?null:a||null},E=function(){var a=function(a,b){var c,d,e;if(null!=a&&"*"!==b[0]&&("string"==typeof a&&(a=[a]),"object"==typeof a&&"length"in a))for(c=0,d=a.length;d>c;c++)if(a.hasOwnProperty(c)&&(e=D(a[c]))){if("*"===e){b.length=0,b.push("*");break}-1===x(e,b)&&b.push(e)}},b={always:"always",samedomain:"sameDomain",never:"never"};return function(c,d){var e,f=d.allowScriptAccess;if("string"==typeof f&&(e=f.toLowerCase())&&/^always|samedomain|never$/.test(e))return b[e];var g=D(d.moviePath);null===g&&(g=c);var h=[];a(d.trustedOrigins,h),a(d.trustedDomains,h);var i=h.length;if(i>0){if(1===i&&"*"===h[0])return"always";if(-1!==x(c,h))return 1===i&&c===g?"sameDomain":"always"}return"never"}}(),F=function(a){if(null==a)return[];if(Object.keys)return Object.keys(a);var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b},G=function(a){if(a)for(var b in a)a.hasOwnProperty(b)&&delete a[b];return a},H=function(){var a=!1;if("boolean"==typeof d.disabled)a=d.disabled===!1;else{if("function"==typeof ActiveXObject)try{new ActiveXObject("ShockwaveFlash.ShockwaveFlash")&&(a=!0)}catch(b){}!a&&navigator.mimeTypes["application/x-shockwave-flash"]&&(a=!0)}return a},I=function(a,b){return this instanceof I?(this.id=""+f++,g[this.id]={instance:this,elements:[],handlers:{}},a&&this.clip(a),"undefined"!=typeof b&&(B("new ZeroClipboard(elements, options)",L.debug),I.config(b)),this.options=I.config(),"boolean"!=typeof d.disabled&&(d.disabled=!H()),d.disabled===!1&&d.outdated!==!0&&null===d.bridge&&(d.outdated=!1,d.ready=!1,M()),void 0):new I(a,b)};I.prototype.setText=function(a){return a&&""!==a&&(e["text/plain"]=a,d.ready===!0&&d.bridge&&d.bridge.setText(a)),this},I.prototype.setSize=function(a,b){return d.ready===!0&&d.bridge&&d.bridge.setSize(a,b),this};var J=function(a){d.ready===!0&&d.bridge&&d.bridge.setHandCursor(a)};I.prototype.destroy=function(){this.unclip(),this.off(),delete g[this.id]};var K=function(){var a,b,c,d=[],e=F(g);for(a=0,b=e.length;b>a;a++)c=g[e[a]].instance,c&&c instanceof I&&d.push(c);return d};I.version="1.3.2";var L={swfPath:l,trustedDomains:window.location.host?[window.location.host]:[],cacheBust:!0,forceHandCursor:!1,zIndex:999999999,debug:!0,title:null,autoActivate:!0};I.config=function(a){"object"==typeof a&&null!==a&&C(L,a);{if("string"!=typeof a||!a){var b={};for(var c in L)L.hasOwnProperty(c)&&(b[c]="object"==typeof L[c]&&null!==L[c]?"length"in L[c]?L[c].slice(0):C({},L[c]):L[c]);return b}if(L.hasOwnProperty(a))return L[a]}},I.destroy=function(){I.deactivate();for(var a in g)if(g.hasOwnProperty(a)&&g[a]){var b=g[a].instance;b&&"function"==typeof b.destroy&&b.destroy()}var c=N(d.bridge);c&&c.parentNode&&(c.parentNode.removeChild(c),d.ready=null,d.bridge=null)},I.activate=function(a){c&&(s(c,L.hoverClass),s(c,L.activeClass)),c=a,r(a,L.hoverClass),O();var b=L.title||a.getAttribute("title");if(b){var e=N(d.bridge);e&&e.setAttribute("title",b)}var f=L.forceHandCursor===!0||"pointer"===n(a,"cursor");J(f)},I.deactivate=function(){var a=N(d.bridge);a&&(a.style.left="0px",a.style.top="-9999px",a.removeAttribute("title")),c&&(s(c,L.hoverClass),s(c,L.activeClass),c=null)};var M=function(){var a,b,c=document.getElementById("global-zeroclipboard-html-bridge");if(!c){var e=I.config();e.jsModuleId="string"==typeof j&&j||"string"==typeof k&&k||null;var f=E(window.location.host,L),g=w(e),h=L.moviePath+v(L.moviePath,L),i=' ';c=document.createElement("div"),c.id="global-zeroclipboard-html-bridge",c.setAttribute("class","global-zeroclipboard-container"),c.style.position="absolute",c.style.left="0px",c.style.top="-9999px",c.style.width="15px",c.style.height="15px",c.style.zIndex=""+A(L.zIndex),document.body.appendChild(c),c.innerHTML=i}a=document["global-zeroclipboard-flash-bridge"],a&&(b=a.length)&&(a=a[b-1]),d.bridge=a||c.children[0].lastElementChild},N=function(a){for(var b=/^OBJECT|EMBED$/,c=a&&a.parentNode;c&&b.test(c.nodeName)&&c.parentNode;)c=c.parentNode;return c||null},O=function(){if(c){var a=u(c,L.zIndex),b=N(d.bridge);b&&(b.style.top=a.top+"px",b.style.left=a.left+"px",b.style.width=a.width+"px",b.style.height=a.height+"px",b.style.zIndex=a.zIndex+1),d.ready===!0&&d.bridge&&d.bridge.setSize(a.width,a.height)}return this};I.prototype.on=function(a,b){var c,e,f,h={},i=g[this.id]&&g[this.id].handlers;if("string"==typeof a&&a)f=a.toLowerCase().split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)a.hasOwnProperty(c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&this.on(c,a[c]);if(f&&f.length){for(c=0,e=f.length;e>c;c++)a=f[c].replace(/^on/,""),h[a]=!0,i[a]||(i[a]=[]),i[a].push(b);h.noflash&&d.disabled&&R.call(this,"noflash",{}),h.wrongflash&&d.outdated&&R.call(this,"wrongflash",{flashVersion:d.version}),h.load&&d.ready&&R.call(this,"load",{flashVersion:d.version})}return this},I.prototype.off=function(a,b){var c,d,e,f,h,i=g[this.id]&&g[this.id].handlers;if(0===arguments.length)f=F(i);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)a.hasOwnProperty(c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&this.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),h=i[a],h&&h.length)if(b)for(e=x(b,h);-1!==e;)h.splice(e,1),e=x(b,h,e);else i[a].length=0;return this},I.prototype.handlers=function(a){var b,c=null,d=g[this.id]&&g[this.id].handlers;if(d){if("string"==typeof a&&a)return d[a]?d[a].slice(0):null;c={};for(b in d)d.hasOwnProperty(b)&&d[b]&&(c[b]=d[b].slice(0))}return c};var P=function(a,b,c,d){var e=g[this.id]&&g[this.id].handlers[a];if(e&&e.length){var f,h,i,j=b||this;for(f=0,h=e.length;h>f;f++)i=e[f],b=j,"string"==typeof i&&"function"==typeof window[i]&&(i=window[i]),"object"==typeof i&&i&&"function"==typeof i.handleEvent&&(b=i,i=i.handleEvent),"function"==typeof i&&z(i,b,c,d)}return this};I.prototype.clip=function(a){a=y(a);for(var b=0;bd;d++)f=g[c[d]].instance,f&&f instanceof I&&h.push(f);return h};L.hoverClass="zeroclipboard-is-hover",L.activeClass="zeroclipboard-is-active",L.trustedOrigins=null,L.allowScriptAccess=null,L.useNoCache=!0,L.moviePath="ZeroClipboard.swf",I.detectFlashSupport=function(){return B("ZeroClipboard.detectFlashSupport",L.debug),H()},I.dispatch=function(a,b){if("string"==typeof a&&a){var d=a.toLowerCase().replace(/^on/,"");if(d)for(var e=c?Q(c):K(),f=0,g=e.length;g>f;f++)R.call(e[f],d,b)}},I.prototype.setHandCursor=function(a){return B("ZeroClipboard.prototype.setHandCursor",L.debug),a="boolean"==typeof a?a:!!a,J(a),L.forceHandCursor=a,this},I.prototype.reposition=function(){return B("ZeroClipboard.prototype.reposition",L.debug),O()},I.prototype.receiveEvent=function(a,b){if(B("ZeroClipboard.prototype.receiveEvent",L.debug),"string"==typeof a&&a){var c=a.toLowerCase().replace(/^on/,"");c&&R.call(this,c,b)}},I.prototype.setCurrent=function(a){return B("ZeroClipboard.prototype.setCurrent",L.debug),I.activate(a),this},I.prototype.resetBridge=function(){return B("ZeroClipboard.prototype.resetBridge",L.debug),I.deactivate(),this},I.prototype.setTitle=function(a){if(B("ZeroClipboard.prototype.setTitle",L.debug),a=a||L.title||c&&c.getAttribute("title")){var b=N(d.bridge);b&&b.setAttribute("title",a)}return this},I.setDefaults=function(a){B("ZeroClipboard.setDefaults",L.debug),I.config(a)},I.prototype.addEventListener=function(a,b){return B("ZeroClipboard.prototype.addEventListener",L.debug),this.on(a,b)},I.prototype.removeEventListener=function(a,b){return B("ZeroClipboard.prototype.removeEventListener",L.debug),this.off(a,b)},I.prototype.ready=function(){return B("ZeroClipboard.prototype.ready",L.debug),d.ready===!0};var R=function(f,g){f=f.toLowerCase().replace(/^on/,"");var h=g&&g.flashVersion&&a(g.flashVersion)||null,i=c,j=!0;switch(f){case"load":if(h){if(!b(h))return R.call(this,"onWrongFlash",{flashVersion:h}),void 0;d.outdated=!1,d.ready=!0,d.version=h}break;case"wrongflash":h&&!b(h)&&(d.outdated=!0,d.ready=!1,d.version=h);break;case"mouseover":r(i,L.hoverClass);break;case"mouseout":L.autoActivate===!0&&I.deactivate();break;case"mousedown":r(i,L.activeClass);break;case"mouseup":s(i,L.activeClass);break;case"datarequested":var k=i.getAttribute("data-clipboard-target"),l=k?document.getElementById(k):null;if(l){var m=l.value||l.textContent||l.innerText;m&&this.setText(m)}else{var n=i.getAttribute("data-clipboard-text");n&&this.setText(n)}j=!1;break;case"complete":G(e)}var o=i,p=[this,g];return P.call(this,f,o,p,j)};"function"==typeof define&&define.amd?define(["require","exports","module"],function(a,b,c){return j=c&&c.id||null,I}):"object"==typeof module&&module&&"object"==typeof module.exports&&module.exports?(k=module.id||null,module.exports=I):window.ZeroClipboard=I}(); -------------------------------------------------------------------------------- /front_end/assets/js/css.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("css", function(config, parserConfig) { 2 | "use strict"; 3 | 4 | if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); 5 | 6 | var indentUnit = config.indentUnit, 7 | tokenHooks = parserConfig.tokenHooks, 8 | mediaTypes = parserConfig.mediaTypes || {}, 9 | mediaFeatures = parserConfig.mediaFeatures || {}, 10 | propertyKeywords = parserConfig.propertyKeywords || {}, 11 | colorKeywords = parserConfig.colorKeywords || {}, 12 | valueKeywords = parserConfig.valueKeywords || {}, 13 | fontProperties = parserConfig.fontProperties || {}, 14 | allowNested = parserConfig.allowNested; 15 | 16 | var type, override; 17 | function ret(style, tp) { type = tp; return style; } 18 | 19 | // Tokenizers 20 | 21 | function tokenBase(stream, state) { 22 | var ch = stream.next(); 23 | if (tokenHooks[ch]) { 24 | var result = tokenHooks[ch](stream, state); 25 | if (result !== false) return result; 26 | } 27 | if (ch == "@") { 28 | stream.eatWhile(/[\w\\\-]/); 29 | return ret("def", stream.current()); 30 | } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) { 31 | return ret(null, "compare"); 32 | } else if (ch == "\"" || ch == "'") { 33 | state.tokenize = tokenString(ch); 34 | return state.tokenize(stream, state); 35 | } else if (ch == "#") { 36 | stream.eatWhile(/[\w\\\-]/); 37 | return ret("atom", "hash"); 38 | } else if (ch == "!") { 39 | stream.match(/^\s*\w*/); 40 | return ret("keyword", "important"); 41 | } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { 42 | stream.eatWhile(/[\w.%]/); 43 | return ret("number", "unit"); 44 | } else if (ch === "-") { 45 | if (/[\d.]/.test(stream.peek())) { 46 | stream.eatWhile(/[\w.%]/); 47 | return ret("number", "unit"); 48 | } else if (stream.match(/^[^-]+-/)) { 49 | return ret("meta", "meta"); 50 | } 51 | } else if (/[,+>*\/]/.test(ch)) { 52 | return ret(null, "select-op"); 53 | } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { 54 | return ret("qualifier", "qualifier"); 55 | } else if (/[:;{}\[\]\(\)]/.test(ch)) { 56 | return ret(null, ch); 57 | } else if (ch == "u" && stream.match("rl(")) { 58 | stream.backUp(1); 59 | state.tokenize = tokenParenthesized; 60 | return ret("property", "word"); 61 | } else if (/[\w\\\-]/.test(ch)) { 62 | stream.eatWhile(/[\w\\\-]/); 63 | return ret("property", "word"); 64 | } else { 65 | return ret(null, null); 66 | } 67 | } 68 | 69 | function tokenString(quote) { 70 | return function(stream, state) { 71 | var escaped = false, ch; 72 | while ((ch = stream.next()) != null) { 73 | if (ch == quote && !escaped) { 74 | if (quote == ")") stream.backUp(1); 75 | break; 76 | } 77 | escaped = !escaped && ch == "\\"; 78 | } 79 | if (ch == quote || !escaped && quote != ")") state.tokenize = null; 80 | return ret("string", "string"); 81 | }; 82 | } 83 | 84 | function tokenParenthesized(stream, state) { 85 | stream.next(); // Must be '(' 86 | if (!stream.match(/\s*[\"\']/, false)) 87 | state.tokenize = tokenString(")"); 88 | else 89 | state.tokenize = null; 90 | return ret(null, "("); 91 | } 92 | 93 | // Context management 94 | 95 | function Context(type, indent, prev) { 96 | this.type = type; 97 | this.indent = indent; 98 | this.prev = prev; 99 | } 100 | 101 | function pushContext(state, stream, type) { 102 | state.context = new Context(type, stream.indentation() + indentUnit, state.context); 103 | return type; 104 | } 105 | 106 | function popContext(state) { 107 | state.context = state.context.prev; 108 | return state.context.type; 109 | } 110 | 111 | function pass(type, stream, state) { 112 | return states[state.context.type](type, stream, state); 113 | } 114 | function popAndPass(type, stream, state, n) { 115 | for (var i = n || 1; i > 0; i--) 116 | state.context = state.context.prev; 117 | return pass(type, stream, state); 118 | } 119 | 120 | // Parser 121 | 122 | function wordAsValue(stream) { 123 | var word = stream.current().toLowerCase(); 124 | if (valueKeywords.hasOwnProperty(word)) 125 | override = "atom"; 126 | else if (colorKeywords.hasOwnProperty(word)) 127 | override = "keyword"; 128 | else 129 | override = "variable"; 130 | } 131 | 132 | var states = {}; 133 | 134 | states.top = function(type, stream, state) { 135 | if (type == "{") { 136 | return pushContext(state, stream, "block"); 137 | } else if (type == "}" && state.context.prev) { 138 | return popContext(state); 139 | } else if (type == "@media") { 140 | return pushContext(state, stream, "media"); 141 | } else if (type == "@font-face") { 142 | return "font_face_before"; 143 | } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) { 144 | return "keyframes"; 145 | } else if (type && type.charAt(0) == "@") { 146 | return pushContext(state, stream, "at"); 147 | } else if (type == "hash") { 148 | override = "builtin"; 149 | } else if (type == "word") { 150 | override = "tag"; 151 | } else if (type == "variable-definition") { 152 | return "maybeprop"; 153 | } else if (type == "interpolation") { 154 | return pushContext(state, stream, "interpolation"); 155 | } else if (type == ":") { 156 | return "pseudo"; 157 | } else if (allowNested && type == "(") { 158 | return pushContext(state, stream, "params"); 159 | } 160 | return state.context.type; 161 | }; 162 | 163 | states.block = function(type, stream, state) { 164 | if (type == "word") { 165 | if (propertyKeywords.hasOwnProperty(stream.current().toLowerCase())) { 166 | override = "property"; 167 | return "maybeprop"; 168 | } else if (allowNested) { 169 | override = stream.match(/^\s*:/, false) ? "property" : "tag"; 170 | return "block"; 171 | } else { 172 | override += " error"; 173 | return "maybeprop"; 174 | } 175 | } else if (type == "meta") { 176 | return "block"; 177 | } else if (!allowNested && (type == "hash" || type == "qualifier")) { 178 | override = "error"; 179 | return "block"; 180 | } else { 181 | return states.top(type, stream, state); 182 | } 183 | }; 184 | 185 | states.maybeprop = function(type, stream, state) { 186 | if (type == ":") return pushContext(state, stream, "prop"); 187 | return pass(type, stream, state); 188 | }; 189 | 190 | states.prop = function(type, stream, state) { 191 | if (type == ";") return popContext(state); 192 | if (type == "{" && allowNested) return pushContext(state, stream, "propBlock"); 193 | if (type == "}" || type == "{") return popAndPass(type, stream, state); 194 | if (type == "(") return pushContext(state, stream, "parens"); 195 | 196 | if (type == "hash" && !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { 197 | override += " error"; 198 | } else if (type == "word") { 199 | wordAsValue(stream); 200 | } else if (type == "interpolation") { 201 | return pushContext(state, stream, "interpolation"); 202 | } 203 | return "prop"; 204 | }; 205 | 206 | states.propBlock = function(type, _stream, state) { 207 | if (type == "}") return popContext(state); 208 | if (type == "word") { override = "property"; return "maybeprop"; } 209 | return state.context.type; 210 | }; 211 | 212 | states.parens = function(type, stream, state) { 213 | if (type == "{" || type == "}") return popAndPass(type, stream, state); 214 | if (type == ")") return popContext(state); 215 | return "parens"; 216 | }; 217 | 218 | states.pseudo = function(type, stream, state) { 219 | if (type == "word") { 220 | override = "variable-3"; 221 | return state.context.type; 222 | } 223 | return pass(type, stream, state); 224 | }; 225 | 226 | states.media = function(type, stream, state) { 227 | if (type == "(") return pushContext(state, stream, "media_parens"); 228 | if (type == "}") return popAndPass(type, stream, state); 229 | if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top"); 230 | 231 | if (type == "word") { 232 | var word = stream.current().toLowerCase(); 233 | if (word == "only" || word == "not" || word == "and") 234 | override = "keyword"; 235 | else if (mediaTypes.hasOwnProperty(word)) 236 | override = "attribute"; 237 | else if (mediaFeatures.hasOwnProperty(word)) 238 | override = "property"; 239 | else 240 | override = "error"; 241 | } 242 | return state.context.type; 243 | }; 244 | 245 | states.media_parens = function(type, stream, state) { 246 | if (type == ")") return popContext(state); 247 | if (type == "{" || type == "}") return popAndPass(type, stream, state, 2); 248 | return states.media(type, stream, state); 249 | }; 250 | 251 | states.font_face_before = function(type, stream, state) { 252 | if (type == "{") 253 | return pushContext(state, stream, "font_face"); 254 | return pass(type, stream, state); 255 | }; 256 | 257 | states.font_face = function(type, stream, state) { 258 | if (type == "}") return popContext(state); 259 | if (type == "word") { 260 | if (!fontProperties.hasOwnProperty(stream.current().toLowerCase())) 261 | override = "error"; 262 | else 263 | override = "property"; 264 | return "maybeprop"; 265 | } 266 | return "font_face"; 267 | }; 268 | 269 | states.keyframes = function(type, stream, state) { 270 | if (type == "word") { override = "variable"; return "keyframes"; } 271 | if (type == "{") return pushContext(state, stream, "top"); 272 | return pass(type, stream, state); 273 | }; 274 | 275 | states.at = function(type, stream, state) { 276 | if (type == ";") return popContext(state); 277 | if (type == "{" || type == "}") return popAndPass(type, stream, state); 278 | if (type == "word") override = "tag"; 279 | else if (type == "hash") override = "builtin"; 280 | return "at"; 281 | }; 282 | 283 | states.interpolation = function(type, stream, state) { 284 | if (type == "}") return popContext(state); 285 | if (type == "{" || type == ";") return popAndPass(type, stream, state); 286 | if (type != "variable") override = "error"; 287 | return "interpolation"; 288 | }; 289 | 290 | states.params = function(type, stream, state) { 291 | if (type == ")") return popContext(state); 292 | if (type == "{" || type == "}") return popAndPass(type, stream, state); 293 | if (type == "word") wordAsValue(stream); 294 | return "params"; 295 | }; 296 | 297 | return { 298 | startState: function(base) { 299 | return {tokenize: null, 300 | state: "top", 301 | context: new Context("top", base || 0, null)}; 302 | }, 303 | 304 | token: function(stream, state) { 305 | if (!state.tokenize && stream.eatSpace()) return null; 306 | var style = (state.tokenize || tokenBase)(stream, state); 307 | if (style && typeof style == "object") { 308 | type = style[1]; 309 | style = style[0]; 310 | } 311 | override = style; 312 | state.state = states[state.state](type, stream, state); 313 | return override; 314 | }, 315 | 316 | indent: function(state, textAfter) { 317 | var cx = state.context, ch = textAfter && textAfter.charAt(0); 318 | var indent = cx.indent; 319 | if (cx.type == "prop" && ch == "}") cx = cx.prev; 320 | if (cx.prev && 321 | (ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") || 322 | ch == ")" && (cx.type == "parens" || cx.type == "params" || cx.type == "media_parens") || 323 | ch == "{" && (cx.type == "at" || cx.type == "media"))) { 324 | indent = cx.indent - indentUnit; 325 | cx = cx.prev; 326 | } 327 | return indent; 328 | }, 329 | 330 | electricChars: "}", 331 | blockCommentStart: "/*", 332 | blockCommentEnd: "*/", 333 | fold: "brace" 334 | }; 335 | }); 336 | 337 | (function() { 338 | function keySet(array) { 339 | var keys = {}; 340 | for (var i = 0; i < array.length; ++i) { 341 | keys[array[i]] = true; 342 | } 343 | return keys; 344 | } 345 | 346 | var mediaTypes_ = [ 347 | "all", "aural", "braille", "handheld", "print", "projection", "screen", 348 | "tty", "tv", "embossed" 349 | ], mediaTypes = keySet(mediaTypes_); 350 | 351 | var mediaFeatures_ = [ 352 | "width", "min-width", "max-width", "height", "min-height", "max-height", 353 | "device-width", "min-device-width", "max-device-width", "device-height", 354 | "min-device-height", "max-device-height", "aspect-ratio", 355 | "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", 356 | "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", 357 | "max-color", "color-index", "min-color-index", "max-color-index", 358 | "monochrome", "min-monochrome", "max-monochrome", "resolution", 359 | "min-resolution", "max-resolution", "scan", "grid" 360 | ], mediaFeatures = keySet(mediaFeatures_); 361 | 362 | var propertyKeywords_ = [ 363 | "align-content", "align-items", "align-self", "alignment-adjust", 364 | "alignment-baseline", "anchor-point", "animation", "animation-delay", 365 | "animation-direction", "animation-duration", "animation-fill-mode", 366 | "animation-iteration-count", "animation-name", "animation-play-state", 367 | "animation-timing-function", "appearance", "azimuth", "backface-visibility", 368 | "background", "background-attachment", "background-clip", "background-color", 369 | "background-image", "background-origin", "background-position", 370 | "background-repeat", "background-size", "baseline-shift", "binding", 371 | "bleed", "bookmark-label", "bookmark-level", "bookmark-state", 372 | "bookmark-target", "border", "border-bottom", "border-bottom-color", 373 | "border-bottom-left-radius", "border-bottom-right-radius", 374 | "border-bottom-style", "border-bottom-width", "border-collapse", 375 | "border-color", "border-image", "border-image-outset", 376 | "border-image-repeat", "border-image-slice", "border-image-source", 377 | "border-image-width", "border-left", "border-left-color", 378 | "border-left-style", "border-left-width", "border-radius", "border-right", 379 | "border-right-color", "border-right-style", "border-right-width", 380 | "border-spacing", "border-style", "border-top", "border-top-color", 381 | "border-top-left-radius", "border-top-right-radius", "border-top-style", 382 | "border-top-width", "border-width", "bottom", "box-decoration-break", 383 | "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", 384 | "caption-side", "clear", "clip", "color", "color-profile", "column-count", 385 | "column-fill", "column-gap", "column-rule", "column-rule-color", 386 | "column-rule-style", "column-rule-width", "column-span", "column-width", 387 | "columns", "content", "counter-increment", "counter-reset", "crop", "cue", 388 | "cue-after", "cue-before", "cursor", "direction", "display", 389 | "dominant-baseline", "drop-initial-after-adjust", 390 | "drop-initial-after-align", "drop-initial-before-adjust", 391 | "drop-initial-before-align", "drop-initial-size", "drop-initial-value", 392 | "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", 393 | "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", 394 | "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings", 395 | "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust", 396 | "font-stretch", "font-style", "font-synthesis", "font-variant", 397 | "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", 398 | "font-variant-ligatures", "font-variant-numeric", "font-variant-position", 399 | "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", 400 | "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end", 401 | "grid-column-start", "grid-row", "grid-row-end", "grid-row-start", 402 | "grid-template", "grid-template-areas", "grid-template-columns", 403 | "grid-template-rows", "hanging-punctuation", "height", "hyphens", 404 | "icon", "image-orientation", "image-rendering", "image-resolution", 405 | "inline-box-align", "justify-content", "left", "letter-spacing", 406 | "line-break", "line-height", "line-stacking", "line-stacking-ruby", 407 | "line-stacking-shift", "line-stacking-strategy", "list-style", 408 | "list-style-image", "list-style-position", "list-style-type", "margin", 409 | "margin-bottom", "margin-left", "margin-right", "margin-top", 410 | "marker-offset", "marks", "marquee-direction", "marquee-loop", 411 | "marquee-play-count", "marquee-speed", "marquee-style", "max-height", 412 | "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", 413 | "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline", 414 | "outline-color", "outline-offset", "outline-style", "outline-width", 415 | "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", 416 | "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", 417 | "page", "page-break-after", "page-break-before", "page-break-inside", 418 | "page-policy", "pause", "pause-after", "pause-before", "perspective", 419 | "perspective-origin", "pitch", "pitch-range", "play-during", "position", 420 | "presentation-level", "punctuation-trim", "quotes", "region-break-after", 421 | "region-break-before", "region-break-inside", "region-fragment", 422 | "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", 423 | "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", 424 | "ruby-position", "ruby-span", "shape-inside", "shape-outside", "size", 425 | "speak", "speak-as", "speak-header", 426 | "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", 427 | "tab-size", "table-layout", "target", "target-name", "target-new", 428 | "target-position", "text-align", "text-align-last", "text-decoration", 429 | "text-decoration-color", "text-decoration-line", "text-decoration-skip", 430 | "text-decoration-style", "text-emphasis", "text-emphasis-color", 431 | "text-emphasis-position", "text-emphasis-style", "text-height", 432 | "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", 433 | "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", 434 | "text-wrap", "top", "transform", "transform-origin", "transform-style", 435 | "transition", "transition-delay", "transition-duration", 436 | "transition-property", "transition-timing-function", "unicode-bidi", 437 | "vertical-align", "visibility", "voice-balance", "voice-duration", 438 | "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", 439 | "voice-volume", "volume", "white-space", "widows", "width", "word-break", 440 | "word-spacing", "word-wrap", "z-index", "zoom", 441 | // SVG-specific 442 | "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", 443 | "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", 444 | "color-interpolation", "color-interpolation-filters", "color-profile", 445 | "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering", 446 | "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke", 447 | "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", 448 | "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", 449 | "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", 450 | "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode" 451 | ], propertyKeywords = keySet(propertyKeywords_); 452 | 453 | var colorKeywords_ = [ 454 | "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", 455 | "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", 456 | "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", 457 | "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", 458 | "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", 459 | "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", 460 | "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", 461 | "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", 462 | "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", 463 | "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", 464 | "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", 465 | "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", 466 | "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink", 467 | "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", 468 | "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", 469 | "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", 470 | "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", 471 | "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", 472 | "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", 473 | "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", 474 | "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", 475 | "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", 476 | "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", 477 | "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", 478 | "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", 479 | "whitesmoke", "yellow", "yellowgreen" 480 | ], colorKeywords = keySet(colorKeywords_); 481 | 482 | var valueKeywords_ = [ 483 | "above", "absolute", "activeborder", "activecaption", "afar", 484 | "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", 485 | "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", 486 | "arabic-indic", "armenian", "asterisks", "auto", "avoid", "avoid-column", "avoid-page", 487 | "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary", 488 | "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", 489 | "both", "bottom", "break", "break-all", "break-word", "button", "button-bevel", 490 | "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", 491 | "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", 492 | "cell", "center", "checkbox", "circle", "cjk-earthly-branch", 493 | "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", 494 | "col-resize", "collapse", "column", "compact", "condensed", "contain", "content", 495 | "content-box", "context-menu", "continuous", "copy", "cover", "crop", 496 | "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", 497 | "decimal-leading-zero", "default", "default-button", "destination-atop", 498 | "destination-in", "destination-out", "destination-over", "devanagari", 499 | "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", 500 | "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", 501 | "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", 502 | "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", 503 | "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", 504 | "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", 505 | "ethiopic-halehame-gez", "ethiopic-halehame-om-et", 506 | "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", 507 | "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", 508 | "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", 509 | "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", 510 | "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", 511 | "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", 512 | "help", "hidden", "hide", "higher", "highlight", "highlighttext", 513 | "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", 514 | "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", 515 | "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", 516 | "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", 517 | "italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer", 518 | "landscape", "lao", "large", "larger", "left", "level", "lighter", 519 | "line-through", "linear", "lines", "list-item", "listbox", "listitem", 520 | "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", 521 | "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", 522 | "lower-roman", "lowercase", "ltr", "malayalam", "match", 523 | "media-controls-background", "media-current-time-display", 524 | "media-fullscreen-button", "media-mute-button", "media-play-button", 525 | "media-return-to-realtime-button", "media-rewind-button", 526 | "media-seek-back-button", "media-seek-forward-button", "media-slider", 527 | "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", 528 | "media-volume-slider-container", "media-volume-sliderthumb", "medium", 529 | "menu", "menulist", "menulist-button", "menulist-text", 530 | "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", 531 | "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", 532 | "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", 533 | "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", 534 | "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", 535 | "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", 536 | "outside", "outside-shape", "overlay", "overline", "padding", "padding-box", 537 | "painted", "page", "paused", "persian", "plus-darker", "plus-lighter", "pointer", 538 | "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", 539 | "radio", "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region", 540 | "relative", "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", 541 | "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", 542 | "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", 543 | "searchfield-cancel-button", "searchfield-decoration", 544 | "searchfield-results-button", "searchfield-results-decoration", 545 | "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", 546 | "single", "skip-white-space", "slide", "slider-horizontal", 547 | "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", 548 | "small", "small-caps", "small-caption", "smaller", "solid", "somali", 549 | "source-atop", "source-in", "source-out", "source-over", "space", "square", 550 | "square-button", "start", "static", "status-bar", "stretch", "stroke", 551 | "sub", "subpixel-antialiased", "super", "sw-resize", "table", 552 | "table-caption", "table-cell", "table-column", "table-column-group", 553 | "table-footer-group", "table-header-group", "table-row", "table-row-group", 554 | "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", 555 | "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", 556 | "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", 557 | "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", 558 | "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", 559 | "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", 560 | "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", 561 | "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", 562 | "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", 563 | "window", "windowframe", "windowtext", "x-large", "x-small", "xor", 564 | "xx-large", "xx-small" 565 | ], valueKeywords = keySet(valueKeywords_); 566 | 567 | var fontProperties_ = [ 568 | "font-family", "src", "unicode-range", "font-variant", "font-feature-settings", 569 | "font-stretch", "font-weight", "font-style" 570 | ], fontProperties = keySet(fontProperties_); 571 | 572 | var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_).concat(colorKeywords_).concat(valueKeywords_); 573 | CodeMirror.registerHelper("hintWords", "css", allWords); 574 | 575 | function tokenCComment(stream, state) { 576 | var maybeEnd = false, ch; 577 | while ((ch = stream.next()) != null) { 578 | if (maybeEnd && ch == "/") { 579 | state.tokenize = null; 580 | break; 581 | } 582 | maybeEnd = (ch == "*"); 583 | } 584 | return ["comment", "comment"]; 585 | } 586 | 587 | function tokenSGMLComment(stream, state) { 588 | if (stream.skipTo("-->")) { 589 | stream.match("-->"); 590 | state.tokenize = null; 591 | } else { 592 | stream.skipToEnd(); 593 | } 594 | return ["comment", "comment"]; 595 | } 596 | 597 | CodeMirror.defineMIME("text/css", { 598 | mediaTypes: mediaTypes, 599 | mediaFeatures: mediaFeatures, 600 | propertyKeywords: propertyKeywords, 601 | colorKeywords: colorKeywords, 602 | valueKeywords: valueKeywords, 603 | fontProperties: fontProperties, 604 | tokenHooks: { 605 | "<": function(stream, state) { 606 | if (!stream.match("!--")) return false; 607 | state.tokenize = tokenSGMLComment; 608 | return tokenSGMLComment(stream, state); 609 | }, 610 | "/": function(stream, state) { 611 | if (!stream.eat("*")) return false; 612 | state.tokenize = tokenCComment; 613 | return tokenCComment(stream, state); 614 | } 615 | }, 616 | name: "css" 617 | }); 618 | 619 | CodeMirror.defineMIME("text/x-scss", { 620 | mediaTypes: mediaTypes, 621 | mediaFeatures: mediaFeatures, 622 | propertyKeywords: propertyKeywords, 623 | colorKeywords: colorKeywords, 624 | valueKeywords: valueKeywords, 625 | fontProperties: fontProperties, 626 | allowNested: true, 627 | tokenHooks: { 628 | "/": function(stream, state) { 629 | if (stream.eat("/")) { 630 | stream.skipToEnd(); 631 | return ["comment", "comment"]; 632 | } else if (stream.eat("*")) { 633 | state.tokenize = tokenCComment; 634 | return tokenCComment(stream, state); 635 | } else { 636 | return ["operator", "operator"]; 637 | } 638 | }, 639 | ":": function(stream) { 640 | if (stream.match(/\s*{/)) 641 | return [null, "{"]; 642 | return false; 643 | }, 644 | "$": function(stream) { 645 | stream.match(/^[\w-]+/); 646 | if (stream.match(/^\s*:/, false)) 647 | return ["variable-2", "variable-definition"]; 648 | return ["variable-2", "variable"]; 649 | }, 650 | "#": function(stream) { 651 | if (!stream.eat("{")) return false; 652 | return [null, "interpolation"]; 653 | } 654 | }, 655 | name: "css", 656 | helperType: "scss" 657 | }); 658 | 659 | CodeMirror.defineMIME("text/x-less", { 660 | mediaTypes: mediaTypes, 661 | mediaFeatures: mediaFeatures, 662 | propertyKeywords: propertyKeywords, 663 | colorKeywords: colorKeywords, 664 | valueKeywords: valueKeywords, 665 | fontProperties: fontProperties, 666 | allowNested: true, 667 | tokenHooks: { 668 | "/": function(stream, state) { 669 | if (stream.eat("/")) { 670 | stream.skipToEnd(); 671 | return ["comment", "comment"]; 672 | } else if (stream.eat("*")) { 673 | state.tokenize = tokenCComment; 674 | return tokenCComment(stream, state); 675 | } else { 676 | return ["operator", "operator"]; 677 | } 678 | }, 679 | "@": function(stream) { 680 | if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false; 681 | stream.eatWhile(/[\w\\\-]/); 682 | if (stream.match(/^\s*:/, false)) 683 | return ["variable-2", "variable-definition"]; 684 | return ["variable-2", "variable"]; 685 | }, 686 | "&": function() { 687 | return ["atom", "atom"]; 688 | } 689 | }, 690 | name: "css", 691 | helperType: "less" 692 | }); 693 | })(); 694 | -------------------------------------------------------------------------------- /front_end/assets/js/main.js: -------------------------------------------------------------------------------- 1 | var S2S = S2S || {}; 2 | 3 | S2S.submitForm = function (stylus) { 4 | $.ajax({ 5 | type: "POST", 6 | url: '/ajax', 7 | data: $("#form").serialize(), 8 | success: function (data) { 9 | stylus.getDoc().setValue(data); 10 | }, 11 | error: function () { 12 | alert("Sorry, we were not able to process your code. \n" + 13 | "Please create a new issue and copy/paste your code at the provided link to help us fix the issue. \n"+ 14 | "https://github.com/mojotech/sass2stylus/issues/new") 15 | } 16 | }); 17 | return false; 18 | }; 19 | 20 | S2S.readFile = function (sass, stylus, fileSelector) { 21 | var files = fileSelector.files; 22 | var file = files[0]; 23 | var reader = new FileReader(); 24 | 25 | // If we use onloadend, we need to check the readyState. 26 | reader.onloadend = function (e) { 27 | if (e.target.readyState == FileReader.DONE) { // DONE == 2 28 | sass.getDoc().setValue(e.target.result); 29 | sass.save(); 30 | S2S.submitForm(stylus); 31 | } 32 | }; 33 | 34 | var blob = file.slice(0, file.size); 35 | reader.readAsBinaryString(blob); 36 | } 37 | 38 | S2S.selectText = function (element) { 39 | var doc = document 40 | , text = doc.getElementById(element) 41 | , range, selection 42 | ; 43 | if (doc.body.createTextRange) { //ms 44 | range = doc.body.createTextRange(); 45 | range.moveToElementText(text); 46 | range.select(); 47 | } else if (window.getSelection) { //all others 48 | selection = window.getSelection(); 49 | range = doc.createRange(); 50 | range.selectNodeContents(text); 51 | selection.removeAllRanges(); 52 | selection.addRange(range); 53 | } 54 | } 55 | 56 | var sassPlaceholder = "# write your SASS/SCSS here or upload"+ 57 | "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" 58 | 59 | $(document).ready(function () { 60 | 61 | var copy_btn = new ZeroClipboard( document.getElementById("copy_btn") ), 62 | request_btn = new ZeroClipboard( document.getElementById("request_btn") ), 63 | response_btn = new ZeroClipboard( document.getElementById("response_btn") ); 64 | 65 | copy_btn.on('noflash', function ( client, args ) { 66 | document.documentElement.className += "no_flash"; 67 | }); 68 | 69 | var sass_editor = CodeMirror.fromTextArea(document.getElementById("codemirror_sass"), { 70 | lineNumbers: true, 71 | matchBrackets: true, 72 | mode: "text/x-scss" 73 | }); 74 | sass_editor.getDoc().setValue(sassPlaceholder); 75 | 76 | sass_editor.on('focus', function () { 77 | if( sass_editor.getValue() === sassPlaceholder ) { 78 | sass_editor.getDoc().setValue("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); 79 | } 80 | }); 81 | 82 | var stylus_editor = CodeMirror.fromTextArea(document.getElementById("codemirror_stylus"),{ 83 | lineNumbers: true, 84 | }); 85 | stylus_editor.getDoc().setValue("# your code will be here!"+ 86 | "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") 87 | 88 | copy_btn.on("dataRequested", function (client, args) { 89 | client.setText( stylus_editor.getValue() ); 90 | }); 91 | 92 | var fileSelector = document.getElementById('file_selector'); 93 | 94 | fileSelector.onchange = function () { 95 | if (this.value !== "") { 96 | S2S.readFile(sass_editor, stylus_editor, fileSelector); 97 | } 98 | }; 99 | 100 | $("#upload").click(function (e) { 101 | e.preventDefault(); 102 | $("#file_selector").click(); 103 | }); 104 | 105 | $("#convert").click(function (e) { 106 | e.preventDefault(); 107 | sass_editor.save(); 108 | S2S.submitForm(stylus_editor); 109 | }); 110 | 111 | $("#download_btn").click(function () { 112 | stylus_editor.save(); 113 | document.getElementById("download_form").submit() 114 | }); 115 | 116 | $('.api-code-block').click( function(){ S2S.selectText( $(this).attr('id')) }); 117 | 118 | sass_editor.on('blur', function () { 119 | if( sass_editor.getValue().match(/^[\r\n ]+$/) ) { 120 | sass_editor.getDoc().setValue(sassPlaceholder); 121 | } 122 | }); 123 | 124 | }); 125 | -------------------------------------------------------------------------------- /front_end/assets/js/placeholder.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | CodeMirror.defineOption("placeholder", "", function(cm, val, old) { 3 | var prev = old && old != CodeMirror.Init; 4 | if (val && !prev) { 5 | cm.on("blur", onBlur); 6 | cm.on("change", onChange); 7 | onChange(cm); 8 | } else if (!val && prev) { 9 | cm.off("blur", onBlur); 10 | cm.off("change", onChange); 11 | clearPlaceholder(cm); 12 | var wrapper = cm.getWrapperElement(); 13 | wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); 14 | } 15 | 16 | if (val && !cm.hasFocus()) onBlur(cm); 17 | }); 18 | 19 | function clearPlaceholder(cm) { 20 | if (cm.state.placeholder) { 21 | cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); 22 | cm.state.placeholder = null; 23 | } 24 | } 25 | function setPlaceholder(cm) { 26 | clearPlaceholder(cm); 27 | var elt = cm.state.placeholder = document.createElement("pre"); 28 | elt.style.cssText = "height: 0; overflow: visible"; 29 | elt.className = "CodeMirror-placeholder"; 30 | elt.appendChild(document.createTextNode(cm.getOption("placeholder"))); 31 | cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); 32 | } 33 | 34 | function onBlur(cm) { 35 | if (isEmpty(cm)) setPlaceholder(cm); 36 | } 37 | function onChange(cm) { 38 | var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); 39 | wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); 40 | 41 | if (empty) setPlaceholder(cm); 42 | else clearPlaceholder(cm); 43 | } 44 | 45 | function isEmpty(cm) { 46 | return (cm.lineCount() === 1) && (cm.getLine(0) === ""); 47 | } 48 | })(); 49 | -------------------------------------------------------------------------------- /front_end/assets/js/sass.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("sass", function(config) { 2 | var tokenRegexp = function(words){ 3 | return new RegExp("^" + words.join("|")); 4 | }; 5 | 6 | var keywords = ["true", "false", "null", "auto"]; 7 | var keywordsRegexp = new RegExp("^" + keywords.join("|")); 8 | 9 | var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"]; 10 | var opRegexp = tokenRegexp(operators); 11 | 12 | var pseudoElementsRegexp = /^::?[\w\-]+/; 13 | 14 | var urlTokens = function(stream, state){ 15 | var ch = stream.peek(); 16 | 17 | if (ch === ")"){ 18 | stream.next(); 19 | state.tokenizer = tokenBase; 20 | return "operator"; 21 | }else if (ch === "("){ 22 | stream.next(); 23 | stream.eatSpace(); 24 | 25 | return "operator"; 26 | }else if (ch === "'" || ch === '"'){ 27 | state.tokenizer = buildStringTokenizer(stream.next()); 28 | return "string"; 29 | }else{ 30 | state.tokenizer = buildStringTokenizer(")", false); 31 | return "string"; 32 | } 33 | }; 34 | var multilineComment = function(stream, state) { 35 | if (stream.skipTo("*/")){ 36 | stream.next(); 37 | stream.next(); 38 | state.tokenizer = tokenBase; 39 | }else { 40 | stream.next(); 41 | } 42 | 43 | return "comment"; 44 | }; 45 | 46 | var buildStringTokenizer = function(quote, greedy){ 47 | if(greedy == null){ greedy = true; } 48 | 49 | function stringTokenizer(stream, state){ 50 | var nextChar = stream.next(); 51 | var peekChar = stream.peek(); 52 | var previousChar = stream.string.charAt(stream.pos-2); 53 | 54 | var endingString = ((nextChar !== "\\" && peekChar === quote) || (nextChar === quote && previousChar !== "\\")); 55 | 56 | /* 57 | console.log("previousChar: " + previousChar); 58 | console.log("nextChar: " + nextChar); 59 | console.log("peekChar: " + peekChar); 60 | console.log("ending: " + endingString); 61 | */ 62 | 63 | if (endingString){ 64 | if (nextChar !== quote && greedy) { stream.next(); } 65 | state.tokenizer = tokenBase; 66 | return "string"; 67 | }else if (nextChar === "#" && peekChar === "{"){ 68 | state.tokenizer = buildInterpolationTokenizer(stringTokenizer); 69 | stream.next(); 70 | return "operator"; 71 | }else { 72 | return "string"; 73 | } 74 | } 75 | 76 | return stringTokenizer; 77 | }; 78 | 79 | var buildInterpolationTokenizer = function(currentTokenizer){ 80 | return function(stream, state){ 81 | if (stream.peek() === "}"){ 82 | stream.next(); 83 | state.tokenizer = currentTokenizer; 84 | return "operator"; 85 | }else{ 86 | return tokenBase(stream, state); 87 | } 88 | }; 89 | }; 90 | 91 | var indent = function(state){ 92 | if (state.indentCount == 0){ 93 | state.indentCount++; 94 | var lastScopeOffset = state.scopes[0].offset; 95 | var currentOffset = lastScopeOffset + config.indentUnit; 96 | state.scopes.unshift({ offset:currentOffset }); 97 | } 98 | }; 99 | 100 | var dedent = function(state){ 101 | if (state.scopes.length == 1) { return; } 102 | 103 | state.scopes.shift(); 104 | }; 105 | 106 | var tokenBase = function(stream, state) { 107 | var ch = stream.peek(); 108 | 109 | // Single line Comment 110 | if (stream.match('//')) { 111 | stream.skipToEnd(); 112 | return "comment"; 113 | } 114 | 115 | // Multiline Comment 116 | if (stream.match('/*')){ 117 | state.tokenizer = multilineComment; 118 | return state.tokenizer(stream, state); 119 | } 120 | 121 | // Interpolation 122 | if (stream.match('#{')){ 123 | state.tokenizer = buildInterpolationTokenizer(tokenBase); 124 | return "operator"; 125 | } 126 | 127 | if (ch === "."){ 128 | stream.next(); 129 | 130 | // Match class selectors 131 | if (stream.match(/^[\w-]+/)){ 132 | indent(state); 133 | return "atom"; 134 | }else if (stream.peek() === "#"){ 135 | indent(state); 136 | return "atom"; 137 | }else{ 138 | return "operator"; 139 | } 140 | } 141 | 142 | if (ch === "#"){ 143 | stream.next(); 144 | 145 | // Hex numbers 146 | if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){ 147 | return "number"; 148 | } 149 | 150 | // ID selectors 151 | if (stream.match(/^[\w-]+/)){ 152 | indent(state); 153 | return "atom"; 154 | } 155 | 156 | if (stream.peek() === "#"){ 157 | indent(state); 158 | return "atom"; 159 | } 160 | } 161 | 162 | // Numbers 163 | if (stream.match(/^-?[0-9\.]+/)){ 164 | return "number"; 165 | } 166 | 167 | // Units 168 | if (stream.match(/^(px|em|in)\b/)){ 169 | return "unit"; 170 | } 171 | 172 | if (stream.match(keywordsRegexp)){ 173 | return "keyword"; 174 | } 175 | 176 | if (stream.match(/^url/) && stream.peek() === "("){ 177 | state.tokenizer = urlTokens; 178 | return "atom"; 179 | } 180 | 181 | // Variables 182 | if (ch === "$"){ 183 | stream.next(); 184 | stream.eatWhile(/[\w-]/); 185 | 186 | if (stream.peek() === ":"){ 187 | stream.next(); 188 | return "variable-2"; 189 | }else{ 190 | return "variable-3"; 191 | } 192 | } 193 | 194 | if (ch === "!"){ 195 | stream.next(); 196 | 197 | if (stream.match(/^[\w]+/)){ 198 | return "keyword"; 199 | } 200 | 201 | return "operator"; 202 | } 203 | 204 | if (ch === "="){ 205 | stream.next(); 206 | 207 | // Match shortcut mixin definition 208 | if (stream.match(/^[\w-]+/)){ 209 | indent(state); 210 | return "meta"; 211 | }else { 212 | return "operator"; 213 | } 214 | } 215 | 216 | if (ch === "+"){ 217 | stream.next(); 218 | 219 | // Match shortcut mixin definition 220 | if (stream.match(/^[\w-]+/)){ 221 | return "variable-3"; 222 | }else { 223 | return "operator"; 224 | } 225 | } 226 | 227 | // Indent Directives 228 | if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)){ 229 | indent(state); 230 | return "meta"; 231 | } 232 | 233 | // Other Directives 234 | if (ch === "@"){ 235 | stream.next(); 236 | stream.eatWhile(/[\w-]/); 237 | return "meta"; 238 | } 239 | 240 | // Strings 241 | if (ch === '"' || ch === "'"){ 242 | stream.next(); 243 | state.tokenizer = buildStringTokenizer(ch); 244 | return "string"; 245 | } 246 | 247 | // Pseudo element selectors 248 | if (ch == ':' && stream.match(pseudoElementsRegexp)){ 249 | return "keyword"; 250 | } 251 | 252 | // atoms 253 | if (stream.eatWhile(/[\w-&]/)){ 254 | // matches a property definition 255 | if (stream.peek() === ":" && !stream.match(pseudoElementsRegexp, false)) 256 | return "property"; 257 | else 258 | return "atom"; 259 | } 260 | 261 | if (stream.match(opRegexp)){ 262 | return "operator"; 263 | } 264 | 265 | // If we haven't returned by now, we move 1 character 266 | // and return an error 267 | stream.next(); 268 | return null; 269 | }; 270 | 271 | var tokenLexer = function(stream, state) { 272 | if (stream.sol()){ 273 | state.indentCount = 0; 274 | } 275 | var style = state.tokenizer(stream, state); 276 | var current = stream.current(); 277 | 278 | if (current === "@return"){ 279 | dedent(state); 280 | } 281 | 282 | if (style === "atom"){ 283 | indent(state); 284 | } 285 | 286 | if (style !== null){ 287 | var startOfToken = stream.pos - current.length; 288 | var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount); 289 | 290 | var newScopes = []; 291 | 292 | for (var i = 0; i < state.scopes.length; i++){ 293 | var scope = state.scopes[i]; 294 | 295 | if (scope.offset <= withCurrentIndent){ 296 | newScopes.push(scope); 297 | } 298 | } 299 | 300 | state.scopes = newScopes; 301 | } 302 | 303 | 304 | return style; 305 | }; 306 | 307 | return { 308 | startState: function() { 309 | return { 310 | tokenizer: tokenBase, 311 | scopes: [{offset: 0, type: 'sass'}], 312 | definedVars: [], 313 | definedMixins: [] 314 | }; 315 | }, 316 | token: function(stream, state) { 317 | var style = tokenLexer(stream, state); 318 | 319 | state.lastToken = { style: style, content: stream.current() }; 320 | 321 | return style; 322 | }, 323 | 324 | indent: function(state) { 325 | return state.scopes[0].offset; 326 | } 327 | }; 328 | }); 329 | 330 | CodeMirror.defineMIME("text/x-sass", "sass"); 331 | -------------------------------------------------------------------------------- /front_end/fonts/source-sans/sourcesanspro-bold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/fonts/source-sans/sourcesanspro-bold-webfont.ttf -------------------------------------------------------------------------------- /front_end/fonts/source-sans/sourcesanspro-bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/fonts/source-sans/sourcesanspro-bold-webfont.woff -------------------------------------------------------------------------------- /front_end/fonts/source-sans/sourcesanspro-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/fonts/source-sans/sourcesanspro-regular-webfont.eot -------------------------------------------------------------------------------- /front_end/fonts/source-sans/sourcesanspro-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/fonts/source-sans/sourcesanspro-regular-webfont.ttf -------------------------------------------------------------------------------- /front_end/fonts/source-sans/sourcesanspro-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojotech/sass2stylus/8ad3ad2e622c667837e49e38c6540a95ddbaaaca/front_end/fonts/source-sans/sourcesanspro-regular-webfont.woff -------------------------------------------------------------------------------- /front_end/views/_api.haml: -------------------------------------------------------------------------------- 1 | .api 2 | .col-half 3 | %h3 1. Make a request 4 | %code#request_text.no_flash.api-code-block 5 | stylus_content = RestClient.post('http://sass2stylus.com/api', {'file' => File.new('YOUR-COMPASS-FILE.scss')}) 6 | %button#request_btn.btn.flash_btn{"data-clipboard-target" => 'request_text'} 7 | Copy 8 | .col-half 9 | %h3 2. Parse a response 10 | %code#response_text.no_flash.api-code-block 11 | File.open('YOUR-NEW-STYLUS-FILE.styl', 'w') { |file| file.write(stylus_content) } 12 | %button#response_btn.btn.flash_btn{"data-clipboard-target" => 'response_text'} 13 | Copy 14 | .clear 15 | -------------------------------------------------------------------------------- /front_end/views/_converter.haml: -------------------------------------------------------------------------------- 1 | 2 | .col-half.sass-col.converter-col 3 | %h2.col-header SASS/SCSS 4 | .action-buttons 5 | %input#file_selector{type:'file'} 6 | %button#upload.btn.btn-stretch-row Upload 7 | %button#convert.btn.btn-stretch-row Convert 8 | .inner-padding 9 | %form#form.sass-code.cf{method:'post', action:'/ajax'} 10 | %textarea#codemirror_sass{name:'sass_textarea'} 11 | .col-half.styl-col.converter-col 12 | %h2.col-header STYL 13 | .action-buttons 14 | %button#download_btn.btn-stretch-row.btn.download 15 | Download 16 | %button#copy_btn.btn.btn-stretch-row.copy.flash_btn 17 | Copy 18 | .inner-padding 19 | %form#download_form.styl-code.cf{method:'post', action:'download'} 20 | %textarea#codemirror_stylus{name:'stylus_textarea'} 21 | -------------------------------------------------------------------------------- /front_end/views/_footer.haml: -------------------------------------------------------------------------------- 1 | .base-content 2 | = haml :_api 3 | .footer 4 | .contain 5 | = haml :_share 6 | #gh_btn 7 | %iframe{src: "http://ghbtns.com/github-btn.html?user=mojotech&repo=sass2stylus&type=fork", allowtransparency: "true", frameborder: "0", scrolling: "0", width: "62", height: "20"} 8 | %p.copyright.white 9 | © 2014 MojoTech · All rights reserved. Made by  10 | %a{href: 'http://mojotech.com', target: "_blank"} MojoTech 11 | -------------------------------------------------------------------------------- /front_end/views/_messages.haml: -------------------------------------------------------------------------------- 1 | .messages.error 2 | .container 3 | .row 4 | %p.white.text-center 5 | | This is an error message. 6 | %a{class: 'close white pull-right', href: 'google.com'} × 7 | -------------------------------------------------------------------------------- /front_end/views/_share.haml: -------------------------------------------------------------------------------- 1 | .share 2 | %a.social-network-link.mt-facebook.mt-share-inline-square-sm{href: 'https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fsass2stylus.com'} 3 | %img{src: 'http://mojotech-static.s3.amazonaws.com/facebook@2x.png'} 4 | %a.social-network-link.mt-twitter.mt-share-inline-square-sm{href: 'http://twitter.com/intent/tweet?text=Easily%20convert%20sass%20to%20stylus&url=http%3A%2F%2Fsass2stylus.com'} 5 | %img{src: 'http://mojotech-static.s3.amazonaws.com/twitter@2x.png'} 6 | %a.social-network-link.mt-linkedin.mt-share-inline-square-sm{href: 'http://www.linkedin.com/shareArticle?mini=true&url=http%3A%2F%2Fsass2stylus.com&summary=Easily%20convert%20sass%20to%20stylus'} 7 | %img{src: 'http://mojotech-static.s3.amazonaws.com/linkedin@2x.png'} 8 | %a.social-network-link.mt-google.mt-share-inline-square-sm{href: 'https://plus.google.com/share?url=http%3A%2F%2Fsass2stylus.com'} 9 | %img{src: 'http://mojotech-static.s3.amazonaws.com/google@2x.png'} 10 | -------------------------------------------------------------------------------- /front_end/views/index.haml: -------------------------------------------------------------------------------- 1 | = haml :_messages 2 | = haml :_converter 3 | .clear 4 | -------------------------------------------------------------------------------- /front_end/views/layout.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title SASS to STYLUS - Convert your SASS to STYLUS 5 | 6 | %meta{charset: "utf8"} 7 | %meta{"http-equiv" => "X-UA-Compatible", content: "IE=edge, chrome=1"} 8 | %meta{name: "sass2stylus", content: "Easily convert sass to stylus"} 9 | 10 | %link{rel: "stylesheet", href: "css/codemirror.css"} 11 | %link{rel: "stylesheet", href: "css/master.css"} 12 | 13 | %body 14 | #content 15 | =yield 16 | = haml :_footer 17 | 18 | %script{src: "https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"} 19 | %script{src: "js/codemirror.js"} 20 | %script{src: "js/css.js"} 21 | %script{src: "js/main.js"} 22 | %script{src: "js/placeholder.js"} 23 | %script{src: "js/sass.js"} 24 | %script{src: "js/ZeroClipboard.min.js"} 25 | %script{type: "text/javascript"} 26 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 27 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 28 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 29 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 30 | ga('create', 'UA-5925457-17', 'sass2stylus.com'); 31 | ga('send', 'pageview'); 32 | -------------------------------------------------------------------------------- /functions.yml: -------------------------------------------------------------------------------- 1 | disabled_functions: 2 | - adjust-color 3 | - adjust-hue 4 | - append 5 | - call 6 | - change-color 7 | - comparable 8 | - fade-in 9 | - fade-out 10 | - feature-exists 11 | - function-exists 12 | - global-variable-exists 13 | - ie-hex-str 14 | - index 15 | - keywords 16 | - list-separator 17 | - map-get 18 | - map-remove 19 | - mixin-exists 20 | - nth 21 | - opacify 22 | - percentage 23 | - quote 24 | - random 25 | - scale-color 26 | - str-index 27 | - str-insert 28 | - str-length 29 | - str-slice 30 | - to-lower-case 31 | - to-upper-case 32 | - transparentize 33 | - unique-id 34 | - unitless 35 | - variable-exists 36 | - zip 37 | -------------------------------------------------------------------------------- /node_converter.rb: -------------------------------------------------------------------------------- 1 | require_relative 'to_stylus.rb' 2 | 3 | puts ToStylus::convert(ARGV[0]) 4 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sass2stylus", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "atob": { 8 | "version": "2.1.2", 9 | "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", 10 | "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", 11 | "dev": true 12 | }, 13 | "balanced-match": { 14 | "version": "1.0.0", 15 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 16 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 17 | "dev": true 18 | }, 19 | "brace-expansion": { 20 | "version": "1.1.11", 21 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 22 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 23 | "dev": true, 24 | "requires": { 25 | "balanced-match": "^1.0.0", 26 | "concat-map": "0.0.1" 27 | } 28 | }, 29 | "concat-map": { 30 | "version": "0.0.1", 31 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 32 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 33 | "dev": true 34 | }, 35 | "css": { 36 | "version": "2.2.4", 37 | "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", 38 | "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", 39 | "dev": true, 40 | "requires": { 41 | "inherits": "^2.0.3", 42 | "source-map": "^0.6.1", 43 | "source-map-resolve": "^0.5.2", 44 | "urix": "^0.1.0" 45 | }, 46 | "dependencies": { 47 | "source-map": { 48 | "version": "0.6.1", 49 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 50 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 51 | "dev": true 52 | } 53 | } 54 | }, 55 | "css-parse": { 56 | "version": "2.0.0", 57 | "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", 58 | "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", 59 | "dev": true, 60 | "requires": { 61 | "css": "^2.0.0" 62 | } 63 | }, 64 | "debug": { 65 | "version": "3.1.0", 66 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 67 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 68 | "dev": true, 69 | "requires": { 70 | "ms": "2.0.0" 71 | } 72 | }, 73 | "decode-uri-component": { 74 | "version": "0.2.0", 75 | "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", 76 | "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", 77 | "dev": true 78 | }, 79 | "fs.realpath": { 80 | "version": "1.0.0", 81 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 82 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 83 | "dev": true 84 | }, 85 | "glob": { 86 | "version": "7.1.4", 87 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 88 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 89 | "dev": true, 90 | "requires": { 91 | "fs.realpath": "^1.0.0", 92 | "inflight": "^1.0.4", 93 | "inherits": "2", 94 | "minimatch": "^3.0.4", 95 | "once": "^1.3.0", 96 | "path-is-absolute": "^1.0.0" 97 | } 98 | }, 99 | "inflight": { 100 | "version": "1.0.6", 101 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 102 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 103 | "dev": true, 104 | "requires": { 105 | "once": "^1.3.0", 106 | "wrappy": "1" 107 | } 108 | }, 109 | "inherits": { 110 | "version": "2.0.4", 111 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 112 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 113 | "dev": true 114 | }, 115 | "minimatch": { 116 | "version": "3.0.4", 117 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 118 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 119 | "dev": true, 120 | "requires": { 121 | "brace-expansion": "^1.1.7" 122 | } 123 | }, 124 | "minimist": { 125 | "version": "0.0.8", 126 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 127 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 128 | "dev": true 129 | }, 130 | "mkdirp": { 131 | "version": "0.5.1", 132 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 133 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 134 | "dev": true, 135 | "requires": { 136 | "minimist": "0.0.8" 137 | } 138 | }, 139 | "ms": { 140 | "version": "2.0.0", 141 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 142 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 143 | "dev": true 144 | }, 145 | "once": { 146 | "version": "1.4.0", 147 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 148 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 149 | "dev": true, 150 | "requires": { 151 | "wrappy": "1" 152 | } 153 | }, 154 | "path-is-absolute": { 155 | "version": "1.0.1", 156 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 157 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 158 | "dev": true 159 | }, 160 | "resolve-url": { 161 | "version": "0.2.1", 162 | "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", 163 | "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", 164 | "dev": true 165 | }, 166 | "safer-buffer": { 167 | "version": "2.1.2", 168 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 169 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 170 | "dev": true 171 | }, 172 | "sax": { 173 | "version": "1.2.4", 174 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 175 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", 176 | "dev": true 177 | }, 178 | "semver": { 179 | "version": "6.3.0", 180 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 181 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 182 | "dev": true 183 | }, 184 | "source-map": { 185 | "version": "0.7.3", 186 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", 187 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", 188 | "dev": true 189 | }, 190 | "source-map-resolve": { 191 | "version": "0.5.2", 192 | "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", 193 | "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", 194 | "dev": true, 195 | "requires": { 196 | "atob": "^2.1.1", 197 | "decode-uri-component": "^0.2.0", 198 | "resolve-url": "^0.2.1", 199 | "source-map-url": "^0.4.0", 200 | "urix": "^0.1.0" 201 | } 202 | }, 203 | "source-map-url": { 204 | "version": "0.4.0", 205 | "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", 206 | "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", 207 | "dev": true 208 | }, 209 | "stylus": { 210 | "version": "0.54.7", 211 | "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.7.tgz", 212 | "integrity": "sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug==", 213 | "dev": true, 214 | "requires": { 215 | "css-parse": "~2.0.0", 216 | "debug": "~3.1.0", 217 | "glob": "^7.1.3", 218 | "mkdirp": "~0.5.x", 219 | "safer-buffer": "^2.1.2", 220 | "sax": "~1.2.4", 221 | "semver": "^6.0.0", 222 | "source-map": "^0.7.3" 223 | } 224 | }, 225 | "urix": { 226 | "version": "0.1.0", 227 | "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", 228 | "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", 229 | "dev": true 230 | }, 231 | "wrappy": { 232 | "version": "1.0.2", 233 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 234 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 235 | "dev": true 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sass2stylus", 3 | "author": "Jake Buob, Cory Simmons / MojoTech, Andrey Popp", 4 | "main": "sass2stylus.js", 5 | "version": "1.0.0", 6 | "description": "Convert Sass (and SCSS) to Stylus", 7 | "keywords": [ 8 | "sass", 9 | "scss", 10 | "stylus", 11 | "convert" 12 | ], 13 | "repository": [ 14 | { 15 | "type": "git", 16 | "url": "https://github.com/mojotech/sass2stylus.git" 17 | } 18 | ], 19 | "scripts": { 20 | "clean": "rm -rf front_end/public && mkdir front_end/public", 21 | "watch": "npm run clean && npm run copy && npm run css:watch", 22 | "build": "npm run clean && npm run copy && npm run css:build", 23 | "copy": "npm run js:copy && npm run css:copy", 24 | "js:copy": "mkdir front_end/public/js && cp -r front_end/assets/js front_end/public", 25 | "css:copy": "mkdir front_end/public/css && cp front_end/assets/css/codemirror.css front_end/public/css/codemirror.css && cp -r front_end/fonts front_end/public/fonts", 26 | "css:watch": "stylus -w front_end/assets/css/master.styl -o front_end/public/css/master.css", 27 | "css:build": "stylus front_end/assets/css/master.styl -o front_end/public/css/master.css" 28 | }, 29 | "github": "https://github.com/mojotech/sass2stylus", 30 | "preferGlobal": true, 31 | "bin": "./sass2stylus.js", 32 | "devDependencies": { 33 | "stylus": "^0.54.7" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sass2stylus.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var exec = require('child_process').exec, 4 | fs = require('fs'), 5 | cliArg = process.argv, 6 | filename = cliArg[2], 7 | rawFilename = filename.replace(/.scss|.sass/, ''), 8 | sassFile = filename.replace('.scss', '.sass'), 9 | converter = __dirname +'/node_converter.rb', 10 | s2s = function() { 11 | exec('ruby '+ converter +' '+ sassFile, function(error, stdout, stderr) { 12 | console.log('Converting Sass to Stylus'); 13 | fs.writeFileSync(rawFilename + '.styl', stdout); 14 | if(error !== null) { 15 | console.log(error); 16 | } 17 | if(sassFile.match(/.tmp/) && sassFile.match(/.tmp/).length) { 18 | fs.unlinkSync(sassFile); 19 | } 20 | }); 21 | }; 22 | 23 | if(filename.match(/.scss/)) { 24 | // Convert SCSS to Sass 25 | sassFile = sassFile.replace('.sass', '.tmp.sass'); 26 | exec('sass-convert '+ filename +' '+ sassFile, function(error, stdout, stderr) { 27 | s2s(); 28 | if(error !== null) { 29 | console.log(error); 30 | } 31 | }); 32 | } else if(filename.match(/.sass/)) { 33 | s2s(); 34 | } else { 35 | console.log('You didn\'t pass a Sass (or SCSS) file.\nUsage: sass2stylus foo.scss'); 36 | } 37 | -------------------------------------------------------------------------------- /spec/fixtures/argument_splat.sass: -------------------------------------------------------------------------------- 1 | @function emCalc($values...) 2 | $max: length($values) 3 | -------------------------------------------------------------------------------- /spec/fixtures/argument_splat.scss: -------------------------------------------------------------------------------- 1 | @function emCalc($values...) { 2 | $max: length($values); 3 | } 4 | -------------------------------------------------------------------------------- /spec/fixtures/argument_splat.styl: -------------------------------------------------------------------------------- 1 | emCalc($values...) 2 | $max = length($values) 3 | -------------------------------------------------------------------------------- /spec/fixtures/atroot.sass: -------------------------------------------------------------------------------- 1 | .parent 2 | @at-root (without: media) 3 | .child1 4 | height: 100px 5 | -------------------------------------------------------------------------------- /spec/fixtures/atroot.scss: -------------------------------------------------------------------------------- 1 | .parent { 2 | @at-root (without: media){ 3 | .child1 { 4 | height: 100px; 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/fixtures/atroot.styl: -------------------------------------------------------------------------------- 1 | //Below is a list of the Sass rules that could not be converted to Stylus 2 | // @at-root: line 2 in your Sass file 3 | 4 | 5 | .parent 6 | //Stylus does not support @at-root 7 | //@at-root (without: media) 8 | // .child1 9 | // height: 100px 10 | -------------------------------------------------------------------------------- /spec/fixtures/charset.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .foo { 4 | color: red; 5 | } 6 | -------------------------------------------------------------------------------- /spec/fixtures/charset.styl: -------------------------------------------------------------------------------- 1 | .foo 2 | color: red 3 | -------------------------------------------------------------------------------- /spec/fixtures/comments.scss: -------------------------------------------------------------------------------- 1 | h1{ 2 | //This is a comment 3 | //This is another comment 4 | color :blue; 5 | h2{ 6 | /*This is 7 | *a 8 | *comment block 9 | */ 10 | color: red; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spec/fixtures/comments.styl: -------------------------------------------------------------------------------- 1 | h1 2 | //This is a comment 3 | //This is another comment 4 | color: blue 5 | h2 6 | /*This is 7 | *a 8 | *comment block 9 | */ 10 | color: red 11 | -------------------------------------------------------------------------------- /spec/fixtures/content.sass: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 100px) 2 | @content 3 | -------------------------------------------------------------------------------- /spec/fixtures/content.scss: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 100px){ 2 | @content; 3 | } 4 | -------------------------------------------------------------------------------- /spec/fixtures/content.styl: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 100px) 2 | {block} 3 | -------------------------------------------------------------------------------- /spec/fixtures/debug.sass: -------------------------------------------------------------------------------- 1 | @debug 10em + 12em 2 | -------------------------------------------------------------------------------- /spec/fixtures/debug.scss: -------------------------------------------------------------------------------- 1 | @debug 10em + 12em; 2 | -------------------------------------------------------------------------------- /spec/fixtures/debug.styl: -------------------------------------------------------------------------------- 1 | //Below is a list of the Sass rules that could not be converted to Stylus 2 | // @debug: line 1 in your Sass file 3 | 4 | 5 | //Stylus does not support @debug 6 | //@debug 10em + 12em 7 | -------------------------------------------------------------------------------- /spec/fixtures/disabled_functions.sass: -------------------------------------------------------------------------------- 1 | h1 2 | color: adjust-color(red, $blue: 5) 3 | color: adjust-hue(red, 10) 4 | $list: append($list1, $list2) 5 | color: call(rgb, 10, 100, 255) 6 | color: change-color(#102030, $blue: 5) 7 | $bool: comparable(2px, 1px) 8 | $color: fade-in(red, 10) 9 | $color: fade-out(red, 10) 10 | $bool: feature-exists(some-feature) 11 | $bool: function-exists(some-function) 12 | $bool: global-variable-exists(global) 13 | $str: ie-hex-str(#abc) 14 | $index: index(1px solid red, solid) 15 | @mixin foo($args...) 16 | $list: keywords($args) 17 | 18 | $listSep: list-separator(1px 2px 3px) 19 | $map: map-remove(("foo": 1, "bar": 2), "bar") 20 | $bool: mixin-exists(mixin) 21 | $index: nth(10px 20px 30px, 1) 22 | $color: opacify(rgba(0, 0, 0, 0.5), 0.1) 23 | $percent: percentage(0.2) 24 | $str: quote('string') 25 | $number: random(10) 26 | $color: scale-color(hsl(120, 70%, 80%), $lightness: 50%) 27 | $number: str-index(abcd, a) 28 | $str: str-insert('abcd', x, 1) 29 | $number: str-length('foo') 30 | $str: str-slice('abcd', 2, 3) 31 | $str: to-lower-case('lower') 32 | $str: to-upper-case('upper') 33 | $color: transparentize(rgba(0,0,0,0.5), 0.1) 34 | $number: unique-id() 35 | $bool: unitless(100) 36 | $bool: variable-exists(nonexistent) 37 | $list: zip(1px 1px 3px, solid dashed solid, red green blue) 38 | @function emCalc($values...) 39 | @return adjust-color(red, $blue: 5) 40 | -------------------------------------------------------------------------------- /spec/fixtures/disabled_functions.scss: -------------------------------------------------------------------------------- 1 | h1{ 2 | color: adjust-color(red, $blue: 5); 3 | color: adjust-hue(red, 10); 4 | $list: append($list1, $list2); 5 | color: call(rgb, 10, 100, 255); 6 | color: change-color(#102030, $blue: 5); 7 | $bool: comparable(2px, 1px); 8 | $color: fade-in(red, 10); 9 | $color: fade-out(red, 10); 10 | $bool: feature-exists(some-feature); 11 | $bool: function-exists(some-function); 12 | $bool: global-variable-exists(global); 13 | $str: ie-hex-str(#abc); 14 | $index: index(1px solid red, solid); 15 | @mixin foo($args...) { 16 | $list: keywords($args); 17 | } 18 | $listSep: list-separator(1px 2px 3px); 19 | $map: map-remove(("foo": 1, "bar": 2), "bar"); 20 | $bool: mixin-exists(mixin); 21 | $index: nth(10px 20px 30px, 1); 22 | $color: opacify(rgba(0, 0, 0, 0.5), 0.1); 23 | $percent: percentage(0.2); 24 | $str: quote('string'); 25 | $number: random(10); 26 | $color: scale-color(hsl(120, 70%, 80%), $lightness: 50%); 27 | $number: str-index(abcd, a); 28 | $str: str-insert('abcd', x, 1); 29 | $number: str-length('foo'); 30 | $str: str-slice('abcd', 2, 3); 31 | $str: to-lower-case('lower'); 32 | $str: to-upper-case('upper'); 33 | $color: transparentize(rgba(0,0,0,0.5), 0.1); 34 | $number: unique-id(); 35 | $bool: unitless(100); 36 | $bool: variable-exists(nonexistent); 37 | $list: zip(1px 1px 3px, solid dashed solid, red green blue); 38 | @function emCalc($values...) { 39 | @return adjust-color(red, $blue: 5); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spec/fixtures/disabled_functions.styl: -------------------------------------------------------------------------------- 1 | //Below is a list of the Sass rules that could not be converted to Stylus 2 | //adjust-color: line 2 in your Sass file 3 | //adjust-hue: line 3 in your Sass file 4 | //append: line 4 in your Sass file 5 | //call: line 5 in your Sass file 6 | //change-color: line 6 in your Sass file 7 | //comparable: line 7 in your Sass file 8 | //fade-in: line 8 in your Sass file 9 | //fade-out: line 9 in your Sass file 10 | //feature-exists: line 10 in your Sass file 11 | //function-exists: line 11 in your Sass file 12 | //global-variable-exists: line 12 in your Sass file 13 | //ie-hex-str: line 13 in your Sass file 14 | //index: line 14 in your Sass file 15 | //keywords: line 16 in your Sass file 16 | //list-separator: line 18 in your Sass file 17 | //map-remove: line 19 in your Sass file 18 | //mixin-exists: line 20 in your Sass file 19 | //nth: line 21 in your Sass file 20 | //opacify: line 22 in your Sass file 21 | //percentage: line 23 in your Sass file 22 | //quote: line 24 in your Sass file 23 | //random: line 25 in your Sass file 24 | //scale-color: line 26 in your Sass file 25 | //index: line 27 in your Sass file 26 | //str-insert: line 28 in your Sass file 27 | //str-length: line 29 in your Sass file 28 | //str-slice: line 30 in your Sass file 29 | //to-lower-case: line 31 in your Sass file 30 | //to-upper-case: line 32 in your Sass file 31 | //transparentize: line 33 in your Sass file 32 | //unique-id: line 34 in your Sass file 33 | //unitless: line 35 in your Sass file 34 | //variable-exists: line 36 in your Sass file 35 | //zip: line 37 in your Sass file 36 | //adjust-color: line 39 in your Sass file 37 | 38 | 39 | h1 40 | //Function adjust-color is not supported in Stylus 41 | //color: adjust-color(red, $blue: 5) 42 | //Function adjust-hue is not supported in Stylus 43 | //color: adjust-hue(red, 10) 44 | //Function append is not supported in Stylus 45 | //$list = append($list1, $list2) 46 | //Function call is not supported in Stylus 47 | //color: call(rgb, 10, 100, 255) 48 | //Function change-color is not supported in Stylus 49 | //color: change-color(#102030, $blue: 5) 50 | //Function comparable is not supported in Stylus 51 | //$bool = comparable(2px, 1px) 52 | //Function fade-in is not supported in Stylus 53 | //$color = fade-in(red, 10) 54 | //Function fade-out is not supported in Stylus 55 | //$color = fade-out(red, 10) 56 | //Function feature-exists is not supported in Stylus 57 | //$bool = feature-exists(some-feature) 58 | //Function function-exists is not supported in Stylus 59 | //$bool = function-exists(some-function) 60 | //Function global-variable-exists is not supported in Stylus 61 | //$bool = global-variable-exists(global) 62 | //Function ie-hex-str is not supported in Stylus 63 | //$str = ie-hex-str(#aabbcc) 64 | //Function index is not supported in Stylus 65 | //$index = index(1px solid red, solid) 66 | foo() 67 | //Function keywords is not supported in Stylus 68 | //$list = keywords($args) 69 | //Function list-separator is not supported in Stylus 70 | //$listSep = list-separator(1px 2px 3px) 71 | //Function map-remove is not supported in Stylus 72 | //$map = map-remove(("foo": 1, "bar": 2), "bar") 73 | //Function mixin-exists is not supported in Stylus 74 | //$bool = mixin-exists(mixin) 75 | //Function nth is not supported in Stylus 76 | //$index = nth(10px 20px 30px, 1) 77 | //Function opacify is not supported in Stylus 78 | //$color = opacify(rgba(0, 0, 0, 0.5), 0.1) 79 | //Function percentage is not supported in Stylus 80 | //$percent = percentage(0.2) 81 | //Function quote is not supported in Stylus 82 | //$str = quote("string") 83 | //Function random is not supported in Stylus 84 | //$number = random(10) 85 | //Function scale-color is not supported in Stylus 86 | //$color = scale-color(hsl(120, 70%, 80%), $lightness: 50%) 87 | //Function index is not supported in Stylus 88 | //$number = str-index(abcd, a) 89 | //Function str-insert is not supported in Stylus 90 | //$str = str-insert("abcd", x, 1) 91 | //Function str-length is not supported in Stylus 92 | //$number = str-length("foo") 93 | //Function str-slice is not supported in Stylus 94 | //$str = str-slice("abcd", 2, 3) 95 | //Function to-lower-case is not supported in Stylus 96 | //$str = to-lower-case("lower") 97 | //Function to-upper-case is not supported in Stylus 98 | //$str = to-upper-case("upper") 99 | //Function transparentize is not supported in Stylus 100 | //$color = transparentize(rgba(0, 0, 0, 0.5), 0.1) 101 | //Function unique-id is not supported in Stylus 102 | //$number = unique-id() 103 | //Function unitless is not supported in Stylus 104 | //$bool = unitless(100) 105 | //Function variable-exists is not supported in Stylus 106 | //$bool = variable-exists(nonexistent) 107 | //Function zip is not supported in Stylus 108 | //$list = zip(1px 1px 3px, solid dashed solid, red green blue) 109 | emCalc($values...) 110 | //Function adjust-color is not supported in Stylus 111 | //adjust-color(red, $blue: 5) 112 | -------------------------------------------------------------------------------- /spec/fixtures/each.sass: -------------------------------------------------------------------------------- 1 | $list: one two three four 2 | 3 | =mixy 4 | @each $number, $number2 in $list 5 | #hashtag 6 | background: image-url("#{$number}.png") 7 | 8 | =mixy 9 | @each $number in $list 10 | #hashtag 11 | background: image-url("#{$number}.png") 12 | 13 | @each $animal in puma, sea-slug, egret, salamander 14 | .#{$animal}-icon 15 | width: 10px 16 | 17 | 18 | -------------------------------------------------------------------------------- /spec/fixtures/each.scss: -------------------------------------------------------------------------------- 1 | $list: one two three four; 2 | 3 | @mixin mixy { 4 | @each $number, $number2 in $list { 5 | #hashtag { 6 | background: image-url("#{$number}.png"); 7 | } 8 | } 9 | } 10 | 11 | @mixin mixy { 12 | @each $number in $list { 13 | #hashtag { 14 | background: image-url("#{$number}.png"); 15 | } 16 | } 17 | } 18 | @each $animal in puma, sea-slug, egret, salamander { 19 | .#{$animal}-icon { 20 | width: 10px; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /spec/fixtures/each.styl: -------------------------------------------------------------------------------- 1 | //Below is a list of the Sass rules that could not be converted to Stylus 2 | // @each: line 4 in your Sass file 3 | 4 | 5 | $list = one two three four 6 | mixy() 7 | //Cannot convert multi-variable each loops to Stylus 8 | //@each $number, $number2 in $list 9 | // #hashtag 10 | // background: image-url("#{$number}.png") 11 | mixy() 12 | for $number in $list 13 | #hashtag 14 | background: image-url("{$number}.png") 15 | for $animal in puma sea-slug egret salamander 16 | .{$animal}-icon 17 | width: 10px 18 | -------------------------------------------------------------------------------- /spec/fixtures/extend.sass: -------------------------------------------------------------------------------- 1 | div 2 | @extend .block 3 | 4 | -------------------------------------------------------------------------------- /spec/fixtures/extend.scss: -------------------------------------------------------------------------------- 1 | div{ 2 | @extend .block; 3 | } 4 | -------------------------------------------------------------------------------- /spec/fixtures/extend.styl: -------------------------------------------------------------------------------- 1 | div 2 | @extend .block 3 | -------------------------------------------------------------------------------- /spec/fixtures/font_face.sass: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family: "altisextra_bold" 3 | src: url('fonts/altis_extrabold/altis-extrabold-webfont.eot') 4 | src: url('fonts/altis_extrabold/altis-extrabold-webfont.eot?#iefix') format('embedded-opentype'), url('fonts/altis_extrabold/altis-extrabold-webfont.woff') format('woff'), url('fonts/altis_extrabold/altis-extrabold-webfont.ttf') format('truetype'), url('fonts/altis_extrabold/altis-extrabold-webfont.svg#altisextra_bold') format('svg') 5 | font-weight: normal 6 | font-style: normal 7 | -------------------------------------------------------------------------------- /spec/fixtures/font_face.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "altisextra_bold"; 3 | src: url('fonts/altis_extrabold/altis-extrabold-webfont.eot'); 4 | src: url('fonts/altis_extrabold/altis-extrabold-webfont.eot?#iefix') format('embedded-opentype'), 5 | url('fonts/altis_extrabold/altis-extrabold-webfont.woff') format('woff'), 6 | url('fonts/altis_extrabold/altis-extrabold-webfont.ttf') format('truetype'), 7 | url('fonts/altis_extrabold/altis-extrabold-webfont.svg#altisextra_bold') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | -------------------------------------------------------------------------------- /spec/fixtures/font_face.styl: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family: "altisextra_bold" 3 | src: url("fonts/altis_extrabold/altis-extrabold-webfont.eot") 4 | src: url("fonts/altis_extrabold/altis-extrabold-webfont.eot?#iefix") format("embedded-opentype"), url("fonts/altis_extrabold/altis-extrabold-webfont.woff") format("woff"), url("fonts/altis_extrabold/altis-extrabold-webfont.ttf") format("truetype"), url("fonts/altis_extrabold/altis-extrabold-webfont.svg#altisextra_bold") format("svg") 5 | font-weight: normal 6 | font-style: normal 7 | -------------------------------------------------------------------------------- /spec/fixtures/foo.sass: -------------------------------------------------------------------------------- 1 | =foo 2 | background: red 3 | 4 | .bar 5 | +foo 6 | -------------------------------------------------------------------------------- /spec/fixtures/foo.scss: -------------------------------------------------------------------------------- 1 | @mixin foo() { 2 | background: red; 3 | } 4 | .bar { 5 | @include foo(); 6 | } 7 | -------------------------------------------------------------------------------- /spec/fixtures/foo.styl: -------------------------------------------------------------------------------- 1 | foo() 2 | background: red 3 | .bar 4 | foo() 5 | -------------------------------------------------------------------------------- /spec/fixtures/for.sass: -------------------------------------------------------------------------------- 1 | @for $i from 1 through 10 2 | $emValues: $i 3 | 4 | @for $i from 1 to 10 5 | $emValues: $i 6 | 7 | @for $i from 1 through $num 8 | $emValues: $i 9 | 10 | @for $i from 1 to $num 11 | $emValues: $i 12 | -------------------------------------------------------------------------------- /spec/fixtures/for.scss: -------------------------------------------------------------------------------- 1 | @for $i from 1 through 10 { 2 | $emValues: $i; 3 | } 4 | 5 | @for $i from 1 to 10 { 6 | $emValues: $i; 7 | } 8 | 9 | @for $i from 1 through $num{ 10 | $emValues: $i; 11 | } 12 | 13 | @for $i from 1 to $num{ 14 | $emValues: $i; 15 | } 16 | -------------------------------------------------------------------------------- /spec/fixtures/for.styl: -------------------------------------------------------------------------------- 1 | for $i in (1)..(10) 2 | $emValues = $i 3 | for $i in (1)..(9) 4 | $emValues = $i 5 | for $i in (1)..($num) 6 | $emValues = $i 7 | for $i in (1)..(($num) - 1) 8 | $emValues = $i 9 | -------------------------------------------------------------------------------- /spec/fixtures/import.sass: -------------------------------------------------------------------------------- 1 | @import 'reset.css' 2 | @import url('hello.com') 3 | @import "http://www.mojotech.com" 4 | @import url(.com) 5 | -------------------------------------------------------------------------------- /spec/fixtures/import.scss: -------------------------------------------------------------------------------- 1 | @import 'reset.css'; 2 | @import url('hello.com'); 3 | @import "http://www.mojotech.com"; 4 | @import url(.com); 5 | -------------------------------------------------------------------------------- /spec/fixtures/import.styl: -------------------------------------------------------------------------------- 1 | @import 'reset.css' 2 | @import url("hello.com") 3 | @import url("http://www.mojotech.com") 4 | @import url(.com) 5 | -------------------------------------------------------------------------------- /spec/fixtures/interpolated_key_values.sass: -------------------------------------------------------------------------------- 1 | calc($property, $value) 2 | #{$property}: -webkit-calc(#{$value}) 3 | #{$property}: calc(#{$value}) 4 | -------------------------------------------------------------------------------- /spec/fixtures/interpolated_key_values.scss: -------------------------------------------------------------------------------- 1 | @mixin calc($property, $value) { 2 | #{$property}: -webkit-calc(#{$value}); 3 | #{$property}: calc(#{$value}); 4 | } 5 | -------------------------------------------------------------------------------- /spec/fixtures/interpolated_key_values.styl: -------------------------------------------------------------------------------- 1 | calc($property, $value) 2 | {$property}: -webkit-calc({$value}) 3 | {$property}: calc({$value}) 4 | -------------------------------------------------------------------------------- /spec/fixtures/interpolation.sass: -------------------------------------------------------------------------------- 1 | div 2 | #{$header} 3 | font-size: $size 4 | 5 | -------------------------------------------------------------------------------- /spec/fixtures/interpolation.scss: -------------------------------------------------------------------------------- 1 | div { 2 | #{$header} { 3 | font-size: $size; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /spec/fixtures/interpolation.styl: -------------------------------------------------------------------------------- 1 | div 2 | {$header} 3 | font-size: $size 4 | -------------------------------------------------------------------------------- /spec/fixtures/media_queries.sass: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 100px) 2 | width: 100px 3 | -------------------------------------------------------------------------------- /spec/fixtures/media_queries.scss: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 100px) { 2 | width: 100px; 3 | } 4 | -------------------------------------------------------------------------------- /spec/fixtures/media_queries.styl: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 100px) 2 | width: 100px 3 | -------------------------------------------------------------------------------- /spec/fixtures/multi_line_selectors.sass: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, i, u, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, fieldset, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video 14 | margin: 0 15 | padding: 0 16 | border: 0 17 | vertical-align: baseline 18 | font: inherit 19 | font-size: 100% 20 | -------------------------------------------------------------------------------- /spec/fixtures/multi_line_selectors.scss: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, i, u, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, fieldset, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | vertical-align: baseline; 18 | font: inherit; 19 | font-size: 100%; 20 | } 21 | -------------------------------------------------------------------------------- /spec/fixtures/multi_line_selectors.styl: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, i, u, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, fieldset, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video 14 | margin: 0 15 | padding: 0 16 | border: 0 17 | vertical-align: baseline 18 | font: inherit 19 | font-size: 100% 20 | -------------------------------------------------------------------------------- /spec/fixtures/nested_properties.sass: -------------------------------------------------------------------------------- 1 | .funky 2 | font: 3 | family: fantasy 4 | size: 30em 5 | weight: bold 6 | .doge 7 | font: 25px red 8 | weight: bold 9 | -------------------------------------------------------------------------------- /spec/fixtures/nested_properties.scss: -------------------------------------------------------------------------------- 1 | .funky { 2 | font: { 3 | family: fantasy; 4 | size: 30em; 5 | weight: bold; 6 | } 7 | } 8 | .doge { 9 | font: 25px red { 10 | weight: bold; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spec/fixtures/nested_properties.styl: -------------------------------------------------------------------------------- 1 | .funky 2 | font-family: fantasy 3 | font-size: 30em 4 | font-weight: bold 5 | .doge 6 | font: 25px red 7 | font-weight: bold 8 | -------------------------------------------------------------------------------- /spec/fixtures/placeholder_selectors.sass: -------------------------------------------------------------------------------- 1 | %foo 2 | background: red 3 | h1 4 | @extend %foo 5 | -------------------------------------------------------------------------------- /spec/fixtures/placeholder_selectors.scss: -------------------------------------------------------------------------------- 1 | %foo { 2 | background: red; 3 | } 4 | h1 { 5 | @extend %foo; 6 | } 7 | -------------------------------------------------------------------------------- /spec/fixtures/placeholder_selectors.styl: -------------------------------------------------------------------------------- 1 | $foo 2 | background: red 3 | h1 4 | @extend $foo 5 | -------------------------------------------------------------------------------- /spec/fixtures/prop_negated_variables.sass: -------------------------------------------------------------------------------- 1 | $foo: 10px 2 | .bar 3 | margin: -$foo 4 | -------------------------------------------------------------------------------- /spec/fixtures/prop_negated_variables.scss: -------------------------------------------------------------------------------- 1 | $foo: 10px; 2 | .bar{ 3 | margin: -$foo; 4 | } 5 | -------------------------------------------------------------------------------- /spec/fixtures/prop_negated_variables.styl: -------------------------------------------------------------------------------- 1 | $foo = 10px 2 | .bar 3 | margin: -1*$foo 4 | -------------------------------------------------------------------------------- /spec/fixtures/prop_operations.sass: -------------------------------------------------------------------------------- 1 | $foo: 10px 2 | .bar 3 | margin: $foo/2 4 | -------------------------------------------------------------------------------- /spec/fixtures/prop_operations.scss: -------------------------------------------------------------------------------- 1 | $foo: 10px; 2 | 3 | .bar { 4 | margin: $foo/2; 5 | } 6 | -------------------------------------------------------------------------------- /spec/fixtures/prop_operations.styl: -------------------------------------------------------------------------------- 1 | $foo = 10px 2 | .bar 3 | margin: ($foo / 2) 4 | -------------------------------------------------------------------------------- /spec/fixtures/warn.sass: -------------------------------------------------------------------------------- 1 | =adjust-location($x, $y) 2 | @if unitless($x) 3 | @warn "Assuming #{$x} to be in pixels" 4 | $x: 1px * $x 5 | -------------------------------------------------------------------------------- /spec/fixtures/warn.scss: -------------------------------------------------------------------------------- 1 | @mixin adjust-location($x, $y) { 2 | @if unitless($x) { 3 | @warn "Assuming #{$x} to be in pixels"; 4 | $x: 1px * $x; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/fixtures/warn.styl: -------------------------------------------------------------------------------- 1 | //Below is a list of the Sass rules that could not be converted to Stylus 2 | // @warn: line 3 in your Sass file 3 | 4 | 5 | adjust-location($x, $y) 6 | if unitless($x) 7 | //Stylus does not support @warn 8 | //@warn "Assuming #{$x} to be in pixels" 9 | $x = 1px * $x 10 | -------------------------------------------------------------------------------- /spec/fixtures/while.sass: -------------------------------------------------------------------------------- 1 | @while $types > 0 2 | .while-#{$types} 3 | width: $type-width + $types 4 | $types: $types - 1 5 | -------------------------------------------------------------------------------- /spec/fixtures/while.scss: -------------------------------------------------------------------------------- 1 | @while $types > 0 { 2 | .while-#{$types} { 3 | width: $type-width + $types; 4 | } 5 | $types: $types - 1; 6 | } 7 | -------------------------------------------------------------------------------- /spec/fixtures/while.styl: -------------------------------------------------------------------------------- 1 | //Below is a list of the Sass rules that could not be converted to Stylus 2 | // @while: line 1 in your Sass file 3 | 4 | 5 | //Stylus does not support while loops 6 | //@while $types > 0 7 | // .while-#{$types} 8 | // width: $type-width + $types 9 | // $types: $types - 1 10 | -------------------------------------------------------------------------------- /spec/to_stylus_spec.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(File.join(File.dirname(__FILE__), '..')) 2 | require 'to_stylus' 3 | 4 | describe ToStylus do 5 | before do 6 | @path = File.dirname(__FILE__) 7 | end 8 | 9 | it "converts a sass file to styl" do 10 | ToStylus.convert("#{@path}/fixtures/foo.sass").should eq(File.read("#{@path}/fixtures/foo.styl").chomp) 11 | end 12 | 13 | it "converts a scss file to styl" do 14 | ToStylus.convert("#{@path}/fixtures/foo.scss").should eq(File.read("#{@path}/fixtures/foo.styl").chomp) 15 | end 16 | 17 | it "handles scss extends" do 18 | ToStylus.convert("#{@path}/fixtures/extend.scss").should eq(File.read("#{@path}/fixtures/extend.styl").chomp) 19 | end 20 | 21 | it "handles sass extends" do 22 | ToStylus.convert("#{@path}/fixtures/extend.sass").should eq(File.read("#{@path}/fixtures/extend.styl").chomp) 23 | end 24 | 25 | it "handles scss media queries" do 26 | ToStylus.convert("#{@path}/fixtures/media_queries.scss").should eq(File.read("#{@path}/fixtures/media_queries.styl").chomp) 27 | end 28 | 29 | it "handles sass media queries" do 30 | ToStylus.convert("#{@path}/fixtures/media_queries.sass").should eq(File.read("#{@path}/fixtures/media_queries.styl").chomp) 31 | end 32 | 33 | it "handles scss import" do 34 | ToStylus.convert("#{@path}/fixtures/import.scss").should eq(File.read("#{@path}/fixtures/import.styl").chomp) 35 | end 36 | 37 | it "handles sass import" do 38 | ToStylus.convert("#{@path}/fixtures/import.sass").should eq(File.read("#{@path}/fixtures/import.styl").chomp) 39 | end 40 | 41 | it "handles scss argument splats" do 42 | ToStylus.convert("#{@path}/fixtures/argument_splat.scss").should eq(File.read("#{@path}/fixtures/argument_splat.styl").chomp) 43 | end 44 | 45 | it "handles sass argument splats" do 46 | ToStylus.convert("#{@path}/fixtures/argument_splat.sass").should eq(File.read("#{@path}/fixtures/argument_splat.styl").chomp) 47 | end 48 | 49 | it "converts scss content to {block}" do 50 | ToStylus.convert("#{@path}/fixtures/content.scss").should eq(File.read("#{@path}/fixtures/content.styl").chomp) 51 | end 52 | 53 | it "converts sass content to {block}" do 54 | ToStylus.convert("#{@path}/fixtures/content.sass").should eq(File.read("#{@path}/fixtures/content.styl").chomp) 55 | end 56 | 57 | it "handles sass for loops" do 58 | ToStylus.convert("#{@path}/fixtures/for.sass").should eq(File.read("#{@path}/fixtures/for.styl").chomp) 59 | end 60 | 61 | it "handles scss for loops" do 62 | ToStylus.convert("#{@path}/fixtures/for.scss").should eq(File.read("#{@path}/fixtures/for.styl").chomp) 63 | end 64 | 65 | it "handles sass interpolation" do 66 | ToStylus.convert("#{@path}/fixtures/interpolation.sass").should eq(File.read("#{@path}/fixtures/interpolation.styl").chomp) 67 | end 68 | 69 | it "handles scss interpolation" do 70 | ToStylus.convert("#{@path}/fixtures/interpolation.scss").should eq(File.read("#{@path}/fixtures/interpolation.styl").chomp) 71 | end 72 | 73 | it "converts sass each iteration to for loop" do 74 | ToStylus.convert("#{@path}/fixtures/each.sass").should eq(File.read("#{@path}/fixtures/each.styl").chomp) 75 | end 76 | 77 | it "converts scss each iteration to for loop" do 78 | ToStylus.convert("#{@path}/fixtures/each.scss").should eq(File.read("#{@path}/fixtures/each.styl").chomp) 79 | end 80 | 81 | it "comments out sass while loops" do 82 | ToStylus.convert("#{@path}/fixtures/while.sass").should eq(File.read("#{@path}/fixtures/while.styl").chomp) 83 | end 84 | 85 | it "comments out scss while loops" do 86 | ToStylus.convert("#{@path}/fixtures/while.scss").should eq(File.read("#{@path}/fixtures/while.styl").chomp) 87 | end 88 | 89 | it "comments out sass @at-root" do 90 | ToStylus.convert("#{@path}/fixtures/atroot.sass").should eq(File.read("#{@path}/fixtures/atroot.styl").chomp) 91 | end 92 | 93 | it "comments out scss @at-root" do 94 | ToStylus.convert("#{@path}/fixtures/atroot.scss").should eq(File.read("#{@path}/fixtures/atroot.styl").chomp) 95 | end 96 | 97 | it "comments out sass @debug" do 98 | ToStylus.convert("#{@path}/fixtures/debug.sass").should eq(File.read("#{@path}/fixtures/debug.styl").chomp) 99 | end 100 | 101 | it "comments out scss @debug" do 102 | ToStylus.convert("#{@path}/fixtures/debug.scss").should eq(File.read("#{@path}/fixtures/debug.styl").chomp) 103 | end 104 | 105 | it "comments out sass @warn" do 106 | ToStylus.convert("#{@path}/fixtures/warn.sass").should eq(File.read("#{@path}/fixtures/warn.styl").chomp) 107 | end 108 | 109 | it "comments out scss @warn" do 110 | ToStylus.convert("#{@path}/fixtures/warn.scss").should eq(File.read("#{@path}/fixtures/warn.styl").chomp) 111 | end 112 | 113 | it "handles sass placeholder selectors" do 114 | ToStylus.convert("#{@path}/fixtures/placeholder_selectors.sass").should eq(File.read("#{@path}/fixtures/placeholder_selectors.styl").chomp) 115 | end 116 | 117 | it "comments scss placeholder selectors" do 118 | ToStylus.convert("#{@path}/fixtures/placeholder_selectors.scss").should eq(File.read("#{@path}/fixtures/placeholder_selectors.styl").chomp) 119 | end 120 | 121 | it "handles sass nested properties" do 122 | ToStylus.convert("#{@path}/fixtures/nested_properties.sass").should eq(File.read("#{@path}/fixtures/nested_properties.styl").chomp) 123 | end 124 | 125 | it "handles scss nested properties" do 126 | ToStylus.convert("#{@path}/fixtures/nested_properties.scss").should eq(File.read("#{@path}/fixtures/nested_properties.styl").chomp) 127 | end 128 | 129 | it "handles sass interpolated key values" do 130 | ToStylus.convert("#{@path}/fixtures/interpolated_key_values.sass").should eq(File.read("#{@path}/fixtures/interpolated_key_values.styl").chomp) 131 | end 132 | 133 | it "handles scss interpolated key values" do 134 | ToStylus.convert("#{@path}/fixtures/interpolated_key_values.scss").should eq(File.read("#{@path}/fixtures/interpolated_key_values.styl").chomp) 135 | end 136 | 137 | it "comments out sass unsupported functions" do 138 | ToStylus.convert("#{@path}/fixtures/disabled_functions.sass").should eq(File.read("#{@path}/fixtures/disabled_functions.styl").chomp) 139 | end 140 | 141 | it "comments out scss unsupported functions" do 142 | ToStylus.convert("#{@path}/fixtures/disabled_functions.scss").should eq(File.read("#{@path}/fixtures/disabled_functions.styl").chomp) 143 | end 144 | 145 | it "conversts comments" do 146 | ToStylus.convert("#{@path}/fixtures/comments.scss").should eq(File.read("#{@path}/fixtures/comments.styl").chomp) 147 | end 148 | 149 | it "handles scss @font-face" do 150 | ToStylus.convert("#{@path}/fixtures/font_face.scss").should eq(File.read("#{@path}/fixtures/font_face.styl").chomp) 151 | end 152 | 153 | it "handles sass @font-face " do 154 | ToStylus.convert("#{@path}/fixtures/font_face.sass").should eq(File.read("#{@path}/fixtures/font_face.styl").chomp) 155 | end 156 | 157 | it "wraps scss prop operations in parentheses" do 158 | ToStylus.convert("#{@path}/fixtures/prop_operations.scss").should eq(File.read("#{@path}/fixtures/prop_operations.styl").chomp) 159 | end 160 | 161 | it "wraps sass prop operations in parentheses" do 162 | ToStylus.convert("#{@path}/fixtures/prop_operations.sass").should eq(File.read("#{@path}/fixtures/prop_operations.styl").chomp) 163 | end 164 | 165 | it "converts negated prop variables to operation scss" do 166 | ToStylus.convert("#{@path}/fixtures/prop_negated_variables.scss").should eq(File.read("#{@path}/fixtures/prop_negated_variables.styl").chomp) 167 | end 168 | 169 | it "converts negated prop variables to operation sass" do 170 | ToStylus.convert("#{@path}/fixtures/prop_negated_variables.sass").should eq(File.read("#{@path}/fixtures/prop_negated_variables.styl").chomp) 171 | end 172 | 173 | %w(sass scss).each do |type| 174 | it "handles multi-line selectors for #{type}" do 175 | ToStylus.convert("#{@path}/fixtures/multi_line_selectors.#{type}").should eq(File.read("#{@path}/fixtures/multi_line_selectors.styl").chomp) 176 | end 177 | end 178 | 179 | it "handles Sass charset directive" do 180 | ToStylus.convert("#{@path}/fixtures/charset.scss").should eq(File.read("#{@path}/fixtures/charset.styl").chomp) 181 | end 182 | end 183 | -------------------------------------------------------------------------------- /to_stylus.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Convert SASS/SCSS to Stylus 3 | # Initial work by Andrey Popp (https://github.com/andreypopp) 4 | 5 | require 'sass' 6 | require 'yaml' 7 | 8 | class ToStylus < Sass::Tree::Visitors::Base 9 | @@functions = YAML::load_file(File.join(__dir__ , 'functions.yml')) 10 | 11 | def self.convert(file) 12 | engine = Sass::Engine.for_file(file, {}) 13 | 14 | tree = engine.to_tree 15 | visit(tree) 16 | end 17 | 18 | def visit(node) 19 | if self.class.respond_to? :node_name 20 | method = "visit_#{node.class.node_name}" 21 | else 22 | method = "visit_#{node_name node}" 23 | end 24 | return if node.is_a?(Sass::Tree::CharsetNode) 25 | if self.respond_to?(method, true) 26 | self.send(method, node) {visit_children(node)} 27 | else 28 | if self.class.respond_to? :node_name 29 | raise "unhandled node: '#{node.class.node_name}'" 30 | else 31 | raise "unhandled node: '#{node_name node}'" 32 | end 33 | end 34 | end 35 | 36 | def visit_children(node) 37 | @indent += 1 38 | super(node) 39 | @indent -= 1 40 | end 41 | 42 | def determine_support(node, is_value) 43 | is_value ? (node_class = node.value) : (node_class = node.expr) 44 | if node_class.is_a? Sass::Script::Tree::Funcall 45 | @@functions['disabled_functions'].each do |func| 46 | if !node_class.inspect.match(func.to_s).nil? 47 | @errors.push("//#{func}: line #{node.line} in your Sass file") 48 | emit "//Function #{func} is not supported in Stylus" 49 | return func 50 | end 51 | end 52 | return 53 | end 54 | end 55 | 56 | def visit_prop(node, output="") 57 | func_support = determine_support(node, true) 58 | if !node.children.empty? 59 | output << "#{node.name.join('')}-" 60 | unless node.value.to_sass.empty? 61 | #for nested scss with values, change the last "-" in the output to a ":" to format output correctly 62 | if node.value.is_a? Sass::Script::Tree::Operation 63 | func_output = "#{output}(#{node.value.to_sass})".sub(/(.*)-/, '\1: ').gsub("\#{","{") 64 | elsif node.value.is_a?(Sass::Script::Tree::UnaryOperation) && node.value.operator.to_s == 'minus' 65 | func_output = "#{output}".sub(/(.*)-/, '\1: ') <<"-1*#{node.value.operand.inspect}".gsub("\#{","{") 66 | else 67 | func_output = "#{output}#{node.value.to_sass}".sub(/(.*)-/, '\1: ').gsub("\#{","{") 68 | end 69 | func_support.nil? ? (emit func_output) : (emit "//" << func_output) 70 | end 71 | node.children.each do |child| 72 | visit_prop(child,output) 73 | end 74 | else 75 | unless node.is_a? Sass::Tree::CommentNode 76 | "#{node.name.join("")[0]}" == "#" ? 77 | node_name = "#{output}{#{node.name-[""]}}:".tr('[]','') : node_name = "#{output}#{node.name.join('')}:" 78 | 79 | if node.value.is_a? Sass::Script::Tree::Operation 80 | func_output = node_name << " (#{node.value.to_sass})".gsub("\#{", "{") 81 | elsif node.value.is_a?(Sass::Script::Tree::UnaryOperation) && node.value.operator.to_s == 'minus' 82 | func_output = node_name << " -1*#{node.value.operand.inspect}" 83 | else 84 | func_output = node_name << " #{node.value.to_sass}".gsub("\#{", "{") 85 | end 86 | func_support.nil? ? (emit func_output) : (emit "//" << func_output) 87 | else 88 | visit(node) 89 | end 90 | end 91 | end 92 | 93 | def visit_variable(node) 94 | func_support = determine_support(node, false) 95 | output = "$#{node.name} = #{node.expr.to_sass}" 96 | func_support.nil? ? (emit output) : (emit "//" << output) 97 | end 98 | 99 | def render_arg(arg) 100 | if arg.is_a? Array 101 | var = arg[0] 102 | default = arg[1] 103 | if default 104 | "#{arg[0].to_sass} = #{arg[1].to_sass}" 105 | else 106 | var.to_sass 107 | end 108 | else 109 | arg.to_sass 110 | end 111 | end 112 | 113 | def render_args(args) 114 | args.map { |a| render_arg(a) }.join(', ') 115 | end 116 | 117 | def emit(line) 118 | line = (' ' * @indent) + line 119 | @lines.push line 120 | end 121 | 122 | def visit_if(node, isElse = false) 123 | line = [] 124 | line.push 'else' if isElse 125 | line.push "if #{node.expr.to_sass}" if node.expr 126 | emit line.join(' ') 127 | visit_children(node) 128 | visit_if(node.else, true) if node.else 129 | end 130 | 131 | def visit_return(node) 132 | func_support = determine_support(node, false) 133 | output = node.expr.to_sass 134 | func_support.nil? ? (emit output) : (emit "//" << output) 135 | end 136 | 137 | def visit_comment(node) 138 | node.invisible? ? lines = node.to_sass.lines : lines = node.value.first.split("\n") 139 | lines.each { |line| emit line.tr("\n", "")} 140 | end 141 | 142 | def visit_mixindef(node) 143 | emit "#{node.name}(#{render_args(node.args)})" 144 | visit_children node 145 | end 146 | 147 | def visit_media(node) 148 | emit "@media #{node.query.map{|i| i.inspect}.join}".gsub! /"/, "" 149 | visit_children node 150 | end 151 | 152 | def visit_content(node) 153 | emit '{block}' 154 | end 155 | 156 | def visit_mixin(node) 157 | emit "#{node.name}(#{render_args(node.args)})" 158 | end 159 | 160 | def visit_import(node) 161 | emit "@import '#{node.imported_filename}'" 162 | end 163 | 164 | def visit_cssimport(node) 165 | if node.to_sass.include?("http://") && !node.to_sass.include?("url") 166 | emit "@import url(#{node.uri})" 167 | elsif(node.to_sass.index("\"http") || node.to_sass.index("\'http")) 168 | emit "#{node.to_sass}".chomp! 169 | elsif(node.to_sass.index("http")) 170 | emit "@import #{node.uri}".gsub("(", "(\"").gsub(")", "\")") 171 | else 172 | emit "#{node.to_sass}".chomp! 173 | end 174 | end 175 | 176 | def visit_extend(node) 177 | emit "#{node.to_sass}".gsub("%","$").chomp! 178 | end 179 | 180 | def visit_function(node) 181 | if node.splat.nil? 182 | emit "#{node.name}(#{render_args(node.args)})" 183 | else 184 | node.args.push([node.splat, nil]) 185 | emit "#{node.name}(#{render_args(node.args)}...)" 186 | end 187 | visit_children node 188 | end 189 | 190 | def visit_rule(node) 191 | rule = (node.rule.length == 1 && node.rule[0].is_a?(String)) ? node.rule[0] : node.to_sass.lines[0] 192 | emit "#{rule}".gsub("\#{", "{").gsub("%", "$").chomp 193 | visit_children node 194 | end 195 | 196 | def for_helper(node, from_or_to) 197 | @@functions['disabled_functions'].each do |func| 198 | unless (from_or_to).inspect.match(func.to_s).nil? 199 | @errors.push("//#{func}: line #{node.line} in your Sass file") 200 | emit "//Function #{func} is not supported in Stylus" 201 | end 202 | end 203 | end 204 | 205 | def visit_for(node) 206 | (node.from.is_a? Sass::Script::Tree::Funcall) ? for_helper(node, node.from) : nil 207 | from = node.from.to_sass 208 | 209 | (node.to.is_a? Sass::Script::Tree::Funcall) ? for_helper(node, node.to) : nil 210 | temp_to = node.to.to_sass 211 | 212 | (node.to.is_a? Sass::Script::Tree::Literal) ? exclusive_to = temp_to.to_i - 1 : exclusive_to = "(#{temp_to}) - 1" 213 | node.exclusive ? to = exclusive_to : to = temp_to 214 | 215 | emit "for $#{node.var} in (#{from})..(#{to})" 216 | visit_children node 217 | end 218 | 219 | def visit_directive(node) 220 | emit "#{node.name}" 221 | visit_children node 222 | end 223 | 224 | def comment_out(node) 225 | node.to_sass.lines.each {|l| emit "//#{l}".chomp } 226 | end 227 | 228 | def visit_each(node) 229 | if node.vars.length == 1 230 | emit "for $#{node.vars.first} in #{node.list.to_sass}".gsub(",","") 231 | visit_children node 232 | else 233 | emit "//Cannot convert multi-variable each loops to Stylus" 234 | @errors.push("// @each: line #{node.line} in your Sass file") 235 | comment_out(node) 236 | end 237 | end 238 | 239 | def visit_while(node) 240 | emit "//Stylus does not support while loops" 241 | @errors.push("// @while: line #{node.line} in your Sass file") 242 | comment_out(node) 243 | end 244 | 245 | def visit_atroot(node) 246 | emit "//Stylus does not support @at-root" 247 | @errors.push("// @at-root: line #{node.line} in your Sass file") 248 | comment_out(node) 249 | end 250 | 251 | def visit_debug(node) 252 | emit "//Stylus does not support @debug" 253 | @errors.push("// @debug: line #{node.line} in your Sass file") 254 | comment_out(node) 255 | end 256 | 257 | def visit_warn(node) 258 | emit "//Stylus does not support @warn" 259 | @errors.push("// @warn: line #{node.line} in your Sass file") 260 | comment_out(node) 261 | end 262 | 263 | def visit_root(node) 264 | @indent = -1 265 | @errors = [] 266 | @lines = [] 267 | visit_children(node) 268 | unless @errors.empty? 269 | @errors.unshift("//Below is a list of the Sass rules that could not be converted to Stylus") 270 | @errors.push("\n") 271 | @lines.unshift(*@errors) 272 | end 273 | @lines.join("\n") 274 | end 275 | 276 | end 277 | --------------------------------------------------------------------------------