├── docs ├── CNAME ├── fonts │ ├── slate.eot │ ├── slate.ttf │ ├── slate.woff │ ├── slate.woff2 │ └── slate.svg ├── images │ ├── logo.png │ ├── logo.xcf │ ├── navbar.png │ └── logo_10.png └── stylesheets │ ├── print.css │ └── screen.css ├── .travis.yml ├── .vscode └── settings.json ├── .gitignore ├── docs-source ├── source │ ├── javascripts │ │ ├── all.js │ │ ├── all_nosearch.js │ │ ├── app │ │ │ ├── _search.js │ │ │ ├── _toc.js │ │ │ └── _lang.js │ │ └── lib │ │ │ ├── _jquery.highlight.js │ │ │ ├── _energize.js │ │ │ └── _imagesloaded.min.js │ ├── fonts │ │ ├── slate.eot │ │ ├── slate.ttf │ │ ├── slate.woff │ │ ├── slate.woff2 │ │ └── slate.svg │ ├── images │ │ ├── logo.png │ │ ├── logo.xcf │ │ ├── logo_10.png │ │ └── navbar.png │ ├── stylesheets │ │ ├── _icon-font.scss │ │ ├── print.css.scss │ │ ├── _rtl.scss │ │ ├── _variables.scss │ │ ├── _normalize.scss │ │ └── screen.css.scss │ ├── includes │ │ └── _errors.md │ ├── layouts │ │ └── layout.erb │ └── index.html.md ├── Gemfile ├── docker-build.sh ├── Dockerfile ├── LICENSE ├── lib │ ├── unique_head.rb │ ├── multilang.rb │ └── toc_data.rb ├── config.rb ├── Vagrantfile ├── CODE_OF_CONDUCT.md ├── CHANGELOG.md ├── font-selection.json ├── deploy.sh └── README.md ├── src ├── modules │ ├── utils.js │ ├── errors.js │ └── map.js ├── promise.js ├── events.js ├── monads.js ├── conditional.js ├── index.js └── arrays.js ├── tests ├── props.js ├── conditional.js ├── events.js ├── arrays.js └── index.js ├── .babelrc ├── webpack.config.js ├── misc └── package-version-manager.js ├── package.json ├── README.md └── dist └── bundle.min.js /docs/CNAME: -------------------------------------------------------------------------------- 1 | www.fpromises.io -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 8 4 | - 9 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.autorefresh": true, 3 | "git.enabled": true 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *.lock 4 | *lock.json 5 | docs-source/build 6 | -------------------------------------------------------------------------------- /docs-source/source/javascripts/all.js: -------------------------------------------------------------------------------- 1 | //= require ./all_nosearch 2 | //= require ./app/_search 3 | -------------------------------------------------------------------------------- /docs/fonts/slate.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs/fonts/slate.eot -------------------------------------------------------------------------------- /docs/fonts/slate.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs/fonts/slate.ttf -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs/images/logo.xcf -------------------------------------------------------------------------------- /docs/fonts/slate.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs/fonts/slate.woff -------------------------------------------------------------------------------- /docs/fonts/slate.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs/fonts/slate.woff2 -------------------------------------------------------------------------------- /docs/images/navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs/images/navbar.png -------------------------------------------------------------------------------- /docs/images/logo_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs/images/logo_10.png -------------------------------------------------------------------------------- /docs-source/source/fonts/slate.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs-source/source/fonts/slate.eot -------------------------------------------------------------------------------- /docs-source/source/fonts/slate.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs-source/source/fonts/slate.ttf -------------------------------------------------------------------------------- /docs-source/source/fonts/slate.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs-source/source/fonts/slate.woff -------------------------------------------------------------------------------- /docs-source/source/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs-source/source/images/logo.png -------------------------------------------------------------------------------- /docs-source/source/images/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs-source/source/images/logo.xcf -------------------------------------------------------------------------------- /docs-source/source/fonts/slate.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs-source/source/fonts/slate.woff2 -------------------------------------------------------------------------------- /docs-source/source/images/logo_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs-source/source/images/logo_10.png -------------------------------------------------------------------------------- /docs-source/source/images/navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kaelinator/functional-promises/master/docs-source/source/images/navbar.png -------------------------------------------------------------------------------- /src/modules/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = {isPromiseLike} 2 | 3 | function isPromiseLike(p) { 4 | return p && typeof p.then === 'function' 5 | } 6 | -------------------------------------------------------------------------------- /docs-source/Gemfile: -------------------------------------------------------------------------------- 1 | ruby '>=2.2.0' 2 | source 'https://rubygems.org' 3 | 4 | # Middleman 5 | gem 'middleman', '~>4.2.1' 6 | gem 'middleman-syntax', '~> 3.0.0' 7 | gem 'middleman-autoprefixer', '~> 2.7.0' 8 | gem "middleman-sprockets", "~> 4.1.0" 9 | gem 'rouge', '~> 2.0.5' 10 | gem 'redcarpet', '~> 3.4.0' 11 | gem 'nokogiri', '~> 1.6.8' 12 | -------------------------------------------------------------------------------- /tests/props.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const FP = require('../src') 3 | 4 | test('FP.get(keyName)', t => FP 5 | .resolve({foo: 'bar'}) 6 | .get('foo') 7 | .then(bar => t.is(bar, 'bar')) 8 | ) 9 | 10 | test('FP.set(keyName, value)', t => FP 11 | .resolve({foo: 'bar'}) 12 | .set('foo', 'baz') 13 | .then(obj => t.is(obj.foo, 'baz')) 14 | ) 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "debug": false, 7 | "modules": false, 8 | "useBuiltIns": true, 9 | "targets": { 10 | "browsers": ["last 2 versions", "safari >= 10"] 11 | } 12 | } 13 | ] 14 | ] 15 | // "plugins": ["transform-es2015-modules-commonjs", "babel-plugin-transform-runtime"] 16 | } 17 | -------------------------------------------------------------------------------- /tests/conditional.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const FP = require('../src') 3 | 4 | test('FP.thenIf(true)', t => FP 5 | .resolve(true) 6 | .thenIf(x => x, x => t.truthy(x), () => t.fail())) 7 | 8 | test('FP.thenIf(false)', t => FP 9 | .resolve(false) 10 | .thenIf(x => x, () => t.fail(), x => t.falsy(x))) 11 | 12 | test('FP.thenIf(true) short', t => FP 13 | .resolve(true) 14 | .thenIf(x => t.truthy(x))) 15 | 16 | -------------------------------------------------------------------------------- /docs-source/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #docker run --name ruby24 -e LANG=en_US.UTF-8 -it -w /slate -v $PWD:/slate ruby:2.4.3-jessie sh -c 'bundle install && bundle exec middleman build --clean' 3 | 4 | # cd .. 5 | 6 | docker run -it --rm \ 7 | -v $PWD/docs-source:/slate \ 8 | fpromises-docs:latest 9 | # -v $PWD/docs:/slate/build \ 10 | cp -rav ./docs-source/build/* ./docs/ 11 | # printf 'www.fpromises.io' > ./docs/CNAME 12 | -------------------------------------------------------------------------------- /docs-source/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.4.3-alpine 2 | 3 | MAINTAINER Dan Levy 4 | 5 | ENV LANG=en_US.UTF-8 6 | RUN apk -Uuv add nodejs \ 7 | abuild build-base \ 8 | findutils \ 9 | grep coreutils \ 10 | cmake gcc \ 11 | binutils 12 | 13 | COPY Gemfile* /slate/ 14 | # VOLUME [ "/slate" ] 15 | WORKDIR /slate 16 | 17 | RUN bundle install 18 | 19 | ENTRYPOINT /usr/local/bin/bundler exec middleman build 20 | 21 | -------------------------------------------------------------------------------- /docs-source/source/javascripts/all_nosearch.js: -------------------------------------------------------------------------------- 1 | //= require ./lib/_energize 2 | //= require ./app/_toc 3 | //= require ./app/_lang 4 | 5 | $(function() { 6 | loadToc($('#toc'), '.toc-link', '.toc-list-h2', 10); 7 | setupLanguages($('body').data('languages')); 8 | $('.content').imagesLoaded( function() { 9 | window.recacheHeights(); 10 | window.refreshToc(); 11 | }); 12 | }); 13 | 14 | window.onpopstate = function() { 15 | activateLanguage(getLanguageFromQueryString()); 16 | }; 17 | -------------------------------------------------------------------------------- /docs-source/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2008-2013 Concur Technologies, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | not use this file except in compliance with the License. You may obtain 5 | a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | License for the specific language governing permissions and limitations 13 | under the License. -------------------------------------------------------------------------------- /docs-source/lib/unique_head.rb: -------------------------------------------------------------------------------- 1 | # Unique header generation 2 | require 'middleman-core/renderers/redcarpet' 3 | class UniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML 4 | def initialize 5 | super 6 | @head_count = {} 7 | end 8 | def header(text, header_level) 9 | friendly_text = text.parameterize 10 | @head_count[friendly_text] ||= 0 11 | @head_count[friendly_text] += 1 12 | if @head_count[friendly_text] > 1 13 | friendly_text += "-#{@head_count[friendly_text]}" 14 | end 15 | return "#{text}" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /docs-source/lib/multilang.rb: -------------------------------------------------------------------------------- 1 | module Multilang 2 | def block_code(code, full_lang_name) 3 | if full_lang_name 4 | parts = full_lang_name.split('--') 5 | rouge_lang_name = (parts) ? parts[0] : "" # just parts[0] here causes null ref exception when no language specified 6 | super(code, rouge_lang_name).sub("highlight #{rouge_lang_name}") do |match| 7 | match + " tab-" + full_lang_name 8 | end 9 | else 10 | super(code, full_lang_name) 11 | end 12 | end 13 | end 14 | 15 | require 'middleman-core/renderers/redcarpet' 16 | Middleman::Renderers::MiddlemanRedcarpetHTML.send :include, Multilang 17 | -------------------------------------------------------------------------------- /src/modules/errors.js: -------------------------------------------------------------------------------- 1 | class FunctionalError extends Error { 2 | constructor(msg, options) { 3 | if (typeof msg === 'object' && msg.message) { 4 | options = msg 5 | msg = msg.message 6 | } 7 | super(msg) 8 | if (typeof options === 'object') { 9 | Object.assign(this, options) 10 | } 11 | } 12 | } 13 | class FunctionalUserError extends FunctionalError {} 14 | class FPUnexpectedError extends FunctionalError {} 15 | class FPInputError extends FunctionalError {} 16 | class FPSoftError extends FunctionalError {} 17 | class FPTimeout extends FunctionalError {} 18 | 19 | module.exports = { 20 | FunctionalError, 21 | FunctionalUserError, 22 | FPUnexpectedError, 23 | FPInputError, 24 | FPSoftError, 25 | FPTimeout, 26 | } 27 | -------------------------------------------------------------------------------- /docs-source/lib/toc_data.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | 3 | def toc_data(page_content) 4 | html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content) 5 | 6 | # get a flat list of headers 7 | headers = [] 8 | html_doc.css('h1, h2, h3').each do |header| 9 | headers.push({ 10 | id: header.attribute('id').to_s, 11 | content: header.children, 12 | level: header.name[1].to_i, 13 | children: [] 14 | }) 15 | end 16 | 17 | [3,2].each do |header_level| 18 | header_to_nest = nil 19 | headers = headers.reject do |header| 20 | if header[:level] == header_level 21 | header_to_nest[:children].push header if header_to_nest 22 | true 23 | else 24 | header_to_nest = header if header[:level] < header_level 25 | false 26 | end 27 | end 28 | end 29 | headers 30 | end -------------------------------------------------------------------------------- /src/promise.js: -------------------------------------------------------------------------------- 1 | const FP = require('./') 2 | 3 | module.exports = function _init(FP) { 4 | FP.prototype.all = FP.all = all 5 | FP.prototype.cast = cast 6 | FP.prototype.reject = reject 7 | } 8 | 9 | function all(promises) { 10 | return Array.isArray(promises) ? Promise.all(promises) : allObjectKeys(promises) 11 | } 12 | 13 | function allObjectKeys(object) { 14 | return FP.resolve(Object.keys(object)) 15 | .map(key => FP.all([key, object[key]])) 16 | .reduce((obj, [key, val]) => { 17 | obj[key] = val; 18 | return obj; 19 | }, {}) 20 | } 21 | 22 | function cast(obj) { 23 | return Promise.resolve(obj) 24 | } 25 | 26 | function reject(err) { 27 | // ret._captureStackTrace(); 28 | // ret._rejectCallback(reason, true); 29 | if (err instanceof Error) { 30 | if (this) this._error = err 31 | return Promise.reject(err) 32 | } 33 | throw new Error(`Reject only accepts a new instance of Error!`) 34 | } 35 | -------------------------------------------------------------------------------- /docs-source/source/stylesheets/_icon-font.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'slate'; 3 | src:font-url('slate.eot?-syv14m'); 4 | src:font-url('slate.eot?#iefix-syv14m') format('embedded-opentype'), 5 | font-url('slate.woff2?-syv14m') format('woff2'), 6 | font-url('slate.woff?-syv14m') format('woff'), 7 | font-url('slate.ttf?-syv14m') format('truetype'), 8 | font-url('slate.svg?-syv14m#slate') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | %icon { 14 | font-family: 'slate'; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | } 22 | 23 | %icon-exclamation-sign { 24 | @extend %icon; 25 | content: "\e600"; 26 | } 27 | %icon-info-sign { 28 | @extend %icon; 29 | content: "\e602"; 30 | } 31 | %icon-ok-sign { 32 | @extend %icon; 33 | content: "\e606"; 34 | } 35 | %icon-search { 36 | @extend %icon; 37 | content: "\e607"; 38 | } 39 | -------------------------------------------------------------------------------- /src/events.js: -------------------------------------------------------------------------------- 1 | const {FPInputError} = require('./modules/errors') 2 | 3 | module.exports = function _init(FP) { 4 | FP.prototype.listen = listen 5 | 6 | /** 7 | * 8 | * Shortcut to run the function returned by `.chainEnd()` 9 | * 10 | * @param {any} obj element or event emitter 11 | * @param {any} eventNames list of event names 12 | * @returns FunctionalPromise 13 | */ 14 | function listen(obj, ...eventNames) { 15 | if (typeof eventNames === 'string') eventNames = [eventNames] 16 | if (!obj[obj.addEventListener ? 'addEventListener' : 'on']) { 17 | throw new FPInputError('Input object isn\'t a valid EventEmitter or similar.') 18 | } 19 | 20 | // Sets up the handlers 21 | const handler = this.chainEnd() 22 | // console.log(` > Attaching ${eventNames} handler`, eventNames) 23 | this.cleanupHandles = eventNames.map(eventName => { 24 | obj[obj.addEventListener ? 'addEventListener' : 'on'](eventName, handler) 25 | return () => obj[obj.removeEventListener ? 'removeEventListener' : 'off'](eventName, handler) 26 | }) 27 | 28 | return this 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs-source/source/includes/_errors.md: -------------------------------------------------------------------------------- 1 | # Errors 2 | 3 | 4 | 5 | The Kittn API uses the following error codes: 6 | 7 | 8 | Error Code | Meaning 9 | ---------- | ------- 10 | 400 | Bad Request -- Your request sucks. 11 | 401 | Unauthorized -- Your API key is wrong. 12 | 403 | Forbidden -- The kitten requested is hidden for administrators only. 13 | 404 | Not Found -- The specified kitten could not be found. 14 | 405 | Method Not Allowed -- You tried to access a kitten with an invalid method. 15 | 406 | Not Acceptable -- You requested a format that isn't json. 16 | 410 | Gone -- The kitten requested has been removed from our servers. 17 | 418 | I'm a teapot. 18 | 429 | Too Many Requests -- You're requesting too many kittens! Slow down! 19 | 500 | Internal Server Error -- We had a problem with our server. Try again later. 20 | 503 | Service Unavailable -- We're temporarily offline for maintenance. Please try again later. 21 | -------------------------------------------------------------------------------- /src/monads.js: -------------------------------------------------------------------------------- 1 | const {FPInputError} = require('./modules/errors') 2 | 3 | module.exports = function _init(FP) { 4 | FP.chain = chain 5 | FP.prototype.chainEnd = chainEnd 6 | 7 | /** 8 | * Start 'recording' a chain of commands, after steps defined call `.chainEnd()` 9 | * @returns FunctionalPromise 10 | */ 11 | function chain() { 12 | // create a placeholder/initial promise to hold the steps/chain data 13 | const promise = FP.resolve() 14 | promise.steps = [] 15 | return promise 16 | } 17 | 18 | /** 19 | * Call after starting a `.chain()`. 20 | * 21 | * One of the few non-chainable methods in the API. 22 | * @returns a Function. It runs your functional chain! 23 | */ 24 | function chainEnd() { 25 | 26 | return input => { 27 | return new FP((resolve, reject) => { 28 | const iterator = this.steps[Symbol.iterator]() 29 | 30 | const next = promise => { 31 | const current = iterator.next() 32 | if (current.done) return resolve(promise) 33 | const [fnName, , args] = current.value 34 | return next(promise[fnName](...args)) 35 | } 36 | next(FP.resolve(input)) 37 | }) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/conditional.js: -------------------------------------------------------------------------------- 1 | const {isPromiseLike} = require('./modules/utils') 2 | 3 | module.exports = function _init(FP) { 4 | FP.prototype.tapIf = tapIf 5 | FP.prototype.thenIf = thenIf 6 | FP.prototype._thenIf = _thenIf 7 | FP.thenIf = _thenIf 8 | 9 | function thenIf(cond, ifTrue, ifFalse) { 10 | if (this.steps) return this.addStep('thenIf', [...arguments]) 11 | if (arguments.length === 1) { 12 | ifTrue = cond 13 | cond = x => x 14 | } 15 | if (isPromiseLike(this)) { 16 | return this.then(value => _thenIf(cond, ifTrue, ifFalse)(value)) 17 | } 18 | return _thenIf(cond, ifTrue, ifFalse) 19 | } 20 | 21 | function tapIf(cond, ifTrue, ifFalse) { 22 | if (this.steps) return this.addStep('tapIf', [...arguments]) 23 | if (arguments.length === 1) { 24 | ifTrue = cond 25 | cond = x => x 26 | } 27 | if (isPromiseLike(this)) { 28 | return this.then(value => _thenIf(cond, ifTrue, ifFalse, true)(value)) 29 | } 30 | return _thenIf(cond, ifTrue, ifFalse, true) 31 | } 32 | 33 | function _thenIf(cond = x => x, ifTrue = x => x, ifFalse = () => null, returnValue = false) { 34 | return value => 35 | FP.resolve(cond(value)) 36 | .then(ans => (ans ? ifTrue(value) : ifFalse(value))) 37 | .then(v => (returnValue ? value : v)) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/events.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const FP = require('../src') 3 | const jsdom = require('jsdom') 4 | const EventEmitter = require('events') 5 | 6 | const getFakeWindow = (html) => { 7 | const { JSDOM } = jsdom 8 | const dom = new JSDOM(`${html}`) 9 | const {window} = dom 10 | return {window, dom, document: window.document} 11 | } 12 | 13 | test.cb('FP.chain().listen() EventEmitter', t => { 14 | // const cleanupHandlers = [] 15 | const eventBus = new EventEmitter() 16 | 17 | FP.chain() 18 | .then(event => { 19 | t.truthy(event) 20 | t.truthy(event.worked) 21 | t.end() 22 | }) 23 | .listen(eventBus, 'hit') 24 | eventBus.emit('hit', {worked: true}) 25 | }) 26 | 27 | 28 | test.cb('FP.chain().listen() DOM', t => { 29 | const buttonUi = getFakeWindow(``) 30 | const {document, window} = buttonUi 31 | global.window = window 32 | global.document = document 33 | const btn = document.querySelector('#btn') 34 | 35 | const listenChain = () => { 36 | const cleanupHandlers = [] 37 | FP.chain() 38 | .then(el => { 39 | t.truthy(el) 40 | t.end() 41 | }) 42 | .listen(btn, 'click') 43 | return {btn, cleanupHandlers} 44 | } 45 | Promise.resolve(listenChain()) 46 | .then(({btn}) => { 47 | btn.click() 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /docs-source/config.rb: -------------------------------------------------------------------------------- 1 | # Unique header generation 2 | require './lib/unique_head.rb' 3 | 4 | # Markdown 5 | set :markdown_engine, :redcarpet 6 | set :markdown, 7 | fenced_code_blocks: true, 8 | smartypants: true, 9 | disable_indented_code_blocks: true, 10 | prettify: true, 11 | tables: true, 12 | with_toc_data: true, 13 | no_intra_emphasis: true, 14 | renderer: UniqueHeadCounter 15 | 16 | # Assets 17 | set :css_dir, 'stylesheets' 18 | set :js_dir, 'javascripts' 19 | set :images_dir, 'images' 20 | set :fonts_dir, 'fonts' 21 | 22 | # Activate the syntax highlighter 23 | activate :syntax 24 | ready do 25 | require './lib/multilang.rb' 26 | end 27 | 28 | activate :sprockets 29 | 30 | activate :autoprefixer do |config| 31 | config.browsers = ['last 2 version', 'Firefox ESR'] 32 | config.cascade = false 33 | config.inline = true 34 | end 35 | 36 | # Github pages require relative links 37 | activate :relative_assets 38 | set :relative_links, true 39 | 40 | # Build Configuration 41 | configure :build do 42 | # If you're having trouble with Middleman hanging, commenting 43 | # out the following two lines has been known to help 44 | activate :minify_css 45 | activate :minify_javascript 46 | # activate :relative_assets 47 | # activate :asset_hash 48 | # activate :gzip 49 | end 50 | 51 | # Deploy Configuration 52 | # If you want Middleman to listen on a different port, you can set that below 53 | set :port, 4567 54 | 55 | helpers do 56 | require './lib/toc_data.rb' 57 | end 58 | -------------------------------------------------------------------------------- /tests/arrays.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const FP = require('../src') 3 | 4 | test('FP.map(x * 2)', t => { 5 | return FP.resolve([1, 2, 3, 4, 5]) 6 | .map(x => x * 2) 7 | .then(results => { 8 | t.deepEqual(results, [2, 4, 6, 8, 10]) 9 | }) 10 | }) 11 | 12 | test('FP.map(x * 2).map(x * 2)', t => { 13 | return FP.resolve([1, 2, 3, 4, 5]) 14 | .map(x => x * 2) 15 | .map(x => x * 2) 16 | .then(results => { 17 | // console.warn('results', results) 18 | t.deepEqual(results, [4, 8, 12, 16, 20]) 19 | }) 20 | }) 21 | 22 | test('[...Promise].map(x * 4)', t => { 23 | return FP.resolve([FP.resolve(1), Promise.resolve(2), Promise.resolve(3), Promise.resolve(4), Promise.resolve(5)]) 24 | .map(x => x * 4) 25 | .then(results => { 26 | // console.warn('results', results) 27 | t.deepEqual(results, [4, 8, 12, 16, 20]) 28 | }) 29 | }) 30 | 31 | test('FP.reduce(sum)', t => { 32 | return FP.resolve([1, 2, 3, 4, 5]) 33 | .reduce((total, n) => total + n, 0) 34 | .then(results => { 35 | t.is(results, 15) 36 | }) 37 | }) 38 | 39 | test('FP.filter()', t => { 40 | const isEven = x => x % 2 === 0 41 | return FP.resolve([1, 2, 3, 4, 5]) 42 | .filter(isEven) 43 | .then(results => { 44 | t.deepEqual(results, [2, 4]) 45 | }) 46 | }) 47 | 48 | test('FP.find()', t => { 49 | const isEven = x => x % 2 === 0 50 | return FP.resolve([1, 2, 3, 4, 5]) 51 | .find(isEven) 52 | .then(results => { 53 | t.deepEqual(results, 2) 54 | }) 55 | }) 56 | 57 | 58 | -------------------------------------------------------------------------------- /docs-source/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure(2) do |config| 2 | config.vm.box = "ubuntu/trusty64" 3 | config.vm.network :forwarded_port, guest: 4567, host: 4567 4 | 5 | config.vm.provision "bootstrap", 6 | type: "shell", 7 | inline: <<-SHELL 8 | sudo apt-add-repository ppa:brightbox/ruby-ng 9 | sudo apt-get update 10 | sudo apt-get install -yq ruby2.4 ruby2.4-dev 11 | sudo apt-get install -yq pkg-config build-essential nodejs git libxml2-dev libxslt-dev 12 | sudo apt-get autoremove -yq 13 | gem2.4 install --no-ri --no-rdoc bundler 14 | SHELL 15 | 16 | # add the local user git config to the vm 17 | config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig" 18 | 19 | config.vm.provision "install", 20 | type: "shell", 21 | privileged: false, 22 | inline: <<-SHELL 23 | echo "==============================================" 24 | echo "Installing app dependencies" 25 | cd /vagrant 26 | bundle config build.nokogiri --use-system-libraries 27 | bundle install 28 | SHELL 29 | 30 | config.vm.provision "run", 31 | type: "shell", 32 | privileged: false, 33 | run: "always", 34 | inline: <<-SHELL 35 | echo "==============================================" 36 | echo "Starting up middleman at http://localhost:4567" 37 | echo "If it does not come up, check the ~/middleman.log file for any error messages" 38 | cd /vagrant 39 | bundle exec middleman server --watcher-force-polling --watcher-latency=1 &> ~/middleman.log & 40 | SHELL 41 | end -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const path = require('path') 3 | const dev = process.env.NODE_ENV === 'development' 4 | // const rollupCommonjsPlugin = require('rollup-plugin-commonjs') 5 | 6 | const config = module.exports = { 7 | devtool: dev ? 'cheap-inline-source-map' : undefined, 8 | entry: './src/index.js', 9 | output: { 10 | pathinfo: true, 11 | // filename: path.join(__dirname, 'build', 'index.js'), 12 | path: path.resolve(__dirname, './dist'), 13 | filename: `./bundle${dev ? '' : '.min'}.js`, 14 | }, 15 | module: { 16 | rules: [ 17 | // { 18 | // test: /\.js$/, 19 | // loader: 'rollup-loader', 20 | // // options: [/* custom rollup plugins */] 21 | // // or directly pass rollup options 22 | // // options: { plugins: [] } 23 | // }, 24 | // { 25 | // test: /src\/index.js$/, 26 | // use: [{ 27 | // loader: 'webpack-rollup-loader', 28 | // options: { 29 | // // OPTIONAL: any rollup options (except `entry`) 30 | // // e.g. 31 | // plugins: [rollupCommonjsPlugin()], 32 | // // external: ['moment'] 33 | // }, 34 | // }] 35 | // }, 36 | {test: /\.js$/, use: 'babel-loader'}, 37 | {test: /\.css$/, use: 'css-loader'}, 38 | ], 39 | }, 40 | } 41 | 42 | 43 | 44 | if (!dev) { 45 | config.plugins = config.plugins || [] 46 | config.plugins.push(new webpack.optimize.UglifyJsPlugin({ 47 | compress: { 48 | warnings: false, 49 | drop_console: false, 50 | }, 51 | })) 52 | } -------------------------------------------------------------------------------- /src/modules/map.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = _map 3 | 4 | function _map(iterable, mapper, opts) { 5 | return new Promise((resolve, reject) => { 6 | opts = Object.assign({ 7 | concurrency: Infinity, 8 | }, this.opts || {}, opts) 9 | 10 | if (typeof mapper !== 'function') { 11 | throw new TypeError('Mapper function is required') 12 | } 13 | 14 | const concurrency = opts.concurrency 15 | 16 | if (!(typeof concurrency === 'number' && concurrency >= 1)) { 17 | throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`) 18 | } 19 | 20 | const ret = [] 21 | const iterator = iterable[Symbol.iterator]() 22 | let isRejected = false 23 | let iterableDone = false 24 | let resolvingCount = 0 25 | let currentIdx = 0 26 | 27 | const next = () => { 28 | if (isRejected) return 29 | 30 | const nextItem = iterator.next() 31 | const i = currentIdx 32 | currentIdx++ 33 | 34 | if (nextItem.done) { 35 | iterableDone = true 36 | return resolvingCount === 0 ? resolve(ret) : undefined 37 | } 38 | 39 | resolvingCount++ 40 | 41 | Promise.resolve(nextItem.value) 42 | .then(el => mapper(el, i)) 43 | .then( 44 | val => { 45 | ret[i] = val 46 | resolvingCount-- 47 | next() 48 | }, 49 | err => { 50 | isRejected = true 51 | reject(err) 52 | } 53 | ) 54 | } 55 | 56 | for (let i = 0; i < concurrency; i++) { 57 | next() 58 | if (iterableDone) break 59 | } 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const test = require('ava') 3 | const FP = require('../src') 4 | 5 | test('FP.resolve(true)', t => { 6 | return FP.resolve(true) 7 | .then(x => t.truthy(x)) 8 | }) 9 | 10 | test('FP.resolve(false)', t => { 11 | return FP.resolve(false) 12 | .then(x => t.falsy(x)) 13 | }) 14 | 15 | test('FP.promisify', t => { 16 | const readFile = FP.promisify(require('fs').readFile); 17 | // now `readFile` will return a promise rather than a cb 18 | return readFile(path.resolve(__dirname, '../package.json'), 'utf8') 19 | .then(data => { 20 | data = typeof data === 'string' ? JSON.parse(data) : data 21 | t.truthy(data.name) 22 | t.truthy(data.version) 23 | t.truthy(data.license === 'MIT') 24 | }) 25 | }) 26 | 27 | test('FP.promisifyAll', t => { 28 | const fs = FP.promisifyAll(require('fs')); 29 | // now `readFile` will return a promise rather than a cb 30 | return fs.readFileAsync(path.resolve(__dirname, '../package.json'), 'utf8') 31 | .then(data => { 32 | data = typeof data === 'string' ? JSON.parse(data) : data 33 | t.truthy(data.name) 34 | t.truthy(data.version) 35 | t.truthy(data.license === 'MIT') 36 | }) 37 | }) 38 | 39 | test('FP.unpack resolve', t => { 40 | const asyncFunc = () => { 41 | const { promise, resolve } = FP.unpack() 42 | Promise.resolve(true) 43 | .then(x => resolve(x)) 44 | return promise 45 | } 46 | return asyncFunc() 47 | .then(x => t.truthy(x)) 48 | .catch(err => t.fail()) 49 | }) 50 | 51 | test('FP.unpack reject', t => { 52 | const asyncFunc = () => { 53 | const { promise, reject } = FP.unpack() 54 | Promise.resolve('Error!') 55 | .then(x => reject(x)) 56 | return promise 57 | } 58 | return asyncFunc() 59 | .then(x => t.fail()) 60 | .catch(err => t.truthy(err)) 61 | }) 62 | -------------------------------------------------------------------------------- /misc/package-version-manager.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const pkg = require('../package.json') 4 | const oldVersion = pkg.version; 5 | let pkgPath = path.join(__dirname, '..', '/package.json') 6 | 7 | 8 | if (process.env.DEBUG) console.log(`pkgPath : ${pkgPath}`) 9 | 10 | let verPattern = ''; 11 | 12 | if (process.argv.length >= 3 && /\d/.test(process.argv[2])) { 13 | verPattern = process.argv[2]; 14 | } 15 | 16 | function safeNumRange(a, b, defaultVal = 0) { 17 | return num => Number.isNaN(num) ? 0 : 18 | num < a || num > b 19 | ? defaultVal 20 | : num 21 | } 22 | 23 | function updatedVersion(verString, modifyPattern) { 24 | const verParts = verString.split('.').map(Number); 25 | let pattern = modifyPattern.split(/[^\d]+/g).map(Number) 26 | let [major, minor, revision] = verParts; 27 | while (pattern.length < 3) { 28 | pattern.unshift(0); 29 | } 30 | // console.log('unshifted PATTERN: ', pattern); 31 | let [addMajor, addMinor, addRevision] = pattern; 32 | pattern = pattern.map(safeNumRange(0, 9)); 33 | // console.log('RANGED PATTERN: ', pattern); 34 | return `${major + addMajor}.${minor + addMinor}.${revision + addRevision}`; 35 | } 36 | 37 | // console.log(`initial vs updated ver: ${pkg.version} -> ${updatedVersion}`) 38 | pkg.version = updatedVersion(pkg.version, verPattern); 39 | const json = JSON.stringify(pkg, null, 2); 40 | 41 | if (process.env.DEBUG) { console.log('Saving: ', json) } 42 | 43 | const pkgPathParts = pkgPath.split('/') 44 | const localPackagePath = '/' + pkgPathParts.slice(pkgPathParts.length - 2).join('/') 45 | 46 | if ( oldVersion === pkg.version) { 47 | console.log(`Skipping version bump for ${localPackagePath} v${oldVersion} == ${pkg.version}`) 48 | } else { 49 | console.log(`Updating ${localPackagePath} v${oldVersion} -> ${pkg.version}`) 50 | fs.writeFile(pkgPath, json, 'utf8', err => { 51 | if (err) return console.error(`AHHH ERROR WRITING PACKAGE.JSON BACK TO ${pkgPath}.`, err); 52 | console.log(`Successfully bumped to v${pkg.version}`); 53 | }) 54 | } 55 | 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functional-promise", 3 | "version": "1.3.7", 4 | "description": "Composition Pipeline Promises Extension. See Complete Docs Here: http://fpromises.io/", 5 | "ava": { 6 | "require": [ 7 | "babel-register" 8 | ], 9 | "babel": "inherit" 10 | }, 11 | "main": "src/index.js", 12 | "browser": "./dist/bundle.min.js", 13 | "repository": { 14 | "url": "https://github.com/justsml/functional-promises.git" 15 | }, 16 | "homepage": "http://fpromises.io/", 17 | "scripts": { 18 | "start": "npm run build && webpack --watch", 19 | "test": "ava --fail-fast=1 --verbose tests", 20 | "docs-docker": "cd docs-source && docker build -t fpromises-docs:latest .", 21 | "docs-build": "./docs-source/docker-build.sh", 22 | "build": "npm run build-dev && npm run build-live", 23 | "build-live": "NODE_ENV=production webpack", 24 | "build-dev": "NODE_ENV=development webpack", 25 | "deploy": "npm run version-bump-revision && npm run build && npm run docs-build && npm run test && npm run publish", 26 | "version-bump-revision": "node ./misc/package-version-manager.js 0.0.1", 27 | "version-bump-minor": "node ./misc/package-version-manager.js 0.1.0" 28 | }, 29 | "author": { 30 | "name": "Dan Levy", 31 | "email": "Dan@DanLevy.net", 32 | "url": "http://www.danlevy.net/" 33 | }, 34 | "license": "MIT", 35 | "dependencies": { 36 | "ava": "^0.24.0", 37 | "babel-loader": "^7.1.2", 38 | "babel-plugin-transform-runtime": "^6.23.0", 39 | "babel-polyfill": "^6.26.0", 40 | "babel-preset-env": "^1.6.1", 41 | "chalk": "^2.3.0", 42 | "jsdom": "^11.5.1", 43 | "lodash": "^4.17.4", 44 | "webpack": "^3.10.0" 45 | }, 46 | "devDependencies": { 47 | "@babel/core": "^7.0.0-beta.35", 48 | "@babel/preset-env": "^7.0.0-beta.35", 49 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 50 | "css-loader": "^0.28.7", 51 | "rollup": "^0.52.3", 52 | "rollup-loader": "^0.3.0", 53 | "rollup-plugin-commonjs": "^8.2.6", 54 | "webpack-rollup-loader": "^0.6.0" 55 | } 56 | } -------------------------------------------------------------------------------- /docs-source/source/javascripts/app/_search.js: -------------------------------------------------------------------------------- 1 | //= require ../lib/_lunr 2 | //= require ../lib/_jquery 3 | //= require ../lib/_jquery.highlight 4 | ;(function () { 5 | 'use strict'; 6 | 7 | var content, searchResults; 8 | var highlightOpts = { element: 'span', className: 'search-highlight' }; 9 | var searchDelay = 0; 10 | var timeoutHandle = 0; 11 | 12 | var index = new lunr.Index(); 13 | 14 | index.ref('id'); 15 | index.field('title', { boost: 10 }); 16 | index.field('body'); 17 | index.pipeline.add(lunr.trimmer, lunr.stopWordFilter); 18 | 19 | $(populate); 20 | $(bind); 21 | 22 | function populate() { 23 | $('h1, h2').each(function() { 24 | var title = $(this); 25 | var body = title.nextUntil('h1, h2'); 26 | index.add({ 27 | id: title.prop('id'), 28 | title: title.text(), 29 | body: body.text() 30 | }); 31 | }); 32 | 33 | determineSearchDelay(); 34 | } 35 | function determineSearchDelay() { 36 | if(index.tokenStore.length>5000) { 37 | searchDelay = 300; 38 | } 39 | } 40 | 41 | function bind() { 42 | content = $('.content'); 43 | searchResults = $('.search-results'); 44 | 45 | $('#input-search').on('keyup',function(e) { 46 | var wait = function() { 47 | return function(executingFunction, waitTime){ 48 | clearTimeout(timeoutHandle); 49 | timeoutHandle = setTimeout(executingFunction, waitTime); 50 | }; 51 | }(); 52 | wait(function(){ 53 | search(e); 54 | }, searchDelay ); 55 | }); 56 | } 57 | 58 | function search(event) { 59 | 60 | var searchInput = $('#input-search')[0]; 61 | 62 | unhighlight(); 63 | searchResults.addClass('visible'); 64 | 65 | // ESC clears the field 66 | if (event.keyCode === 27) searchInput.value = ''; 67 | 68 | if (searchInput.value) { 69 | var results = index.search(searchInput.value).filter(function(r) { 70 | return r.score > 0.0001; 71 | }); 72 | 73 | if (results.length) { 74 | searchResults.empty(); 75 | $.each(results, function (index, result) { 76 | var elem = document.getElementById(result.ref); 77 | searchResults.append("
  • " + $(elem).text() + "
  • "); 78 | }); 79 | highlight.call(searchInput); 80 | } else { 81 | searchResults.html('
  • '); 82 | $('.search-results li').text('No Results Found for "' + searchInput.value + '"'); 83 | } 84 | } else { 85 | unhighlight(); 86 | searchResults.removeClass('visible'); 87 | } 88 | } 89 | 90 | function highlight() { 91 | if (this.value) content.highlight(this.value, highlightOpts); 92 | } 93 | 94 | function unhighlight() { 95 | content.unhighlight(highlightOpts); 96 | } 97 | })(); 98 | 99 | -------------------------------------------------------------------------------- /docs/fonts/slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs-source/source/fonts/slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs-source/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@lord.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /docs-source/source/stylesheets/print.css.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | @import 'normalize'; 3 | @import 'variables'; 4 | @import 'icon-font'; 5 | 6 | /* 7 | Copyright 2008-2013 Concur Technologies, Inc. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); you may 10 | not use this file except in compliance with the License. You may obtain 11 | a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 17 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 18 | License for the specific language governing permissions and limitations 19 | under the License. 20 | */ 21 | 22 | $print-color: #999; 23 | $print-color-light: #ccc; 24 | $print-font-size: 12px; 25 | 26 | body { 27 | @extend %default-font; 28 | } 29 | 30 | .tocify, .toc-footer, .lang-selector, .search, #nav-button { 31 | display: none; 32 | } 33 | 34 | .tocify-wrapper>img { 35 | margin: 0 auto; 36 | display: block; 37 | } 38 | 39 | .content { 40 | font-size: 12px; 41 | 42 | pre, code { 43 | @extend %code-font; 44 | @extend %break-words; 45 | border: 1px solid $print-color; 46 | border-radius: 5px; 47 | font-size: 0.8em; 48 | } 49 | 50 | pre { 51 | code { 52 | border: 0; 53 | } 54 | } 55 | 56 | pre { 57 | padding: 1.3em; 58 | } 59 | 60 | code { 61 | padding: 0.2em; 62 | } 63 | 64 | table { 65 | border: 1px solid $print-color; 66 | tr { 67 | border-bottom: 1px solid $print-color; 68 | } 69 | td,th { 70 | padding: 0.7em; 71 | } 72 | } 73 | 74 | p { 75 | line-height: 1.5; 76 | } 77 | 78 | a { 79 | text-decoration: none; 80 | color: #000; 81 | } 82 | 83 | h1 { 84 | @extend %header-font; 85 | font-size: 2.5em; 86 | padding-top: 0.5em; 87 | padding-bottom: 0.5em; 88 | margin-top: 1em; 89 | margin-bottom: $h1-margin-bottom; 90 | border: 2px solid $print-color-light; 91 | border-width: 2px 0; 92 | text-align: center; 93 | } 94 | 95 | h2 { 96 | @extend %header-font; 97 | font-size: 1.8em; 98 | margin-top: 2em; 99 | border-top: 2px solid $print-color-light; 100 | padding-top: 0.8em; 101 | } 102 | 103 | h1+h2, h1+div+h2 { 104 | border-top: none; 105 | padding-top: 0; 106 | margin-top: 0; 107 | } 108 | 109 | h3, h4 { 110 | @extend %header-font; 111 | font-size: 0.8em; 112 | margin-top: 1.5em; 113 | margin-bottom: 0.8em; 114 | text-transform: uppercase; 115 | } 116 | 117 | h5, h6 { 118 | text-transform: uppercase; 119 | } 120 | 121 | aside { 122 | padding: 1em; 123 | border: 1px solid $print-color-light; 124 | border-radius: 5px; 125 | margin-top: 1.5em; 126 | margin-bottom: 1.5em; 127 | line-height: 1.6; 128 | } 129 | 130 | aside:before { 131 | vertical-align: middle; 132 | padding-right: 0.5em; 133 | font-size: 14px; 134 | } 135 | 136 | aside.notice:before { 137 | @extend %icon-info-sign; 138 | } 139 | 140 | aside.warning:before { 141 | @extend %icon-exclamation-sign; 142 | } 143 | 144 | aside.success:before { 145 | @extend %icon-ok-sign; 146 | } 147 | } -------------------------------------------------------------------------------- /docs-source/source/stylesheets/_rtl.scss: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // RTL Styles Variables 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | $default: auto; 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | // TABLE OF CONTENTS 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | #toc>ul>li>a>span { 12 | float: left; 13 | } 14 | 15 | .toc-wrapper { 16 | transition: right 0.3s ease-in-out !important; 17 | left: $default !important; 18 | #{right}: 0; 19 | } 20 | 21 | .toc-h2 { 22 | padding-#{right}: $nav-padding + $nav-indent; 23 | } 24 | 25 | #nav-button { 26 | #{right}: 0; 27 | transition: right 0.3s ease-in-out; 28 | &.open { 29 | right: $nav-width 30 | } 31 | } 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | // PAGE LAYOUT AND CODE SAMPLE BACKGROUND 35 | //////////////////////////////////////////////////////////////////////////////// 36 | .page-wrapper { 37 | margin-#{left}: $default !important; 38 | margin-#{right}: $nav-width; 39 | .dark-box { 40 | #{right}: $default; 41 | #{left}: 0; 42 | } 43 | } 44 | 45 | .lang-selector { 46 | width: $default !important; 47 | a { 48 | float: right; 49 | } 50 | } 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | // CODE SAMPLE STYLES 54 | //////////////////////////////////////////////////////////////////////////////// 55 | .content { 56 | &>h1, 57 | &>h2, 58 | &>h3, 59 | &>h4, 60 | &>h5, 61 | &>h6, 62 | &>p, 63 | &>table, 64 | &>ul, 65 | &>ol, 66 | &>aside, 67 | &>dl { 68 | margin-#{left}: $examples-width; 69 | margin-#{right}: $default !important; 70 | } 71 | &>ul, 72 | &>ol { 73 | padding-#{right}: $main-padding + 15px; 74 | } 75 | table { 76 | th, 77 | td { 78 | text-align: right; 79 | } 80 | } 81 | dd { 82 | margin-#{right}: 15px; 83 | } 84 | aside { 85 | aside:before { 86 | padding-#{left}: 0.5em; 87 | } 88 | .search-highlight { 89 | background: linear-gradient(to top right, #F7E633 0%, #F1D32F 100%); 90 | } 91 | } 92 | pre, 93 | blockquote { 94 | float: left !important; 95 | clear: left !important; 96 | } 97 | } 98 | 99 | //////////////////////////////////////////////////////////////////////////////// 100 | // TYPOGRAPHY 101 | //////////////////////////////////////////////////////////////////////////////// 102 | h1, 103 | h2, 104 | h3, 105 | h4, 106 | h5, 107 | h6, 108 | p, 109 | aside { 110 | text-align: right; 111 | direction: rtl; 112 | } 113 | 114 | .toc-wrapper { 115 | text-align: right; 116 | direction: rtl; 117 | font-weight: 100 !important; 118 | } 119 | 120 | 121 | //////////////////////////////////////////////////////////////////////////////// 122 | // RESPONSIVE DESIGN 123 | //////////////////////////////////////////////////////////////////////////////// 124 | @media (max-width: $tablet-width) { 125 | .toc-wrapper { 126 | #{right}: -$nav-width; 127 | &.open { 128 | #{right}: 0; 129 | } 130 | } 131 | .page-wrapper { 132 | margin-#{right}: 0; 133 | } 134 | } 135 | 136 | @media (max-width: $phone-width) { 137 | %left-col { 138 | margin-#{left}: 0; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /docs-source/source/javascripts/app/_toc.js: -------------------------------------------------------------------------------- 1 | //= require ../lib/_jquery 2 | //= require ../lib/_imagesloaded.min 3 | ;(function () { 4 | 'use strict'; 5 | 6 | var loaded = false; 7 | 8 | var debounce = function(func, waitTime) { 9 | var timeout = false; 10 | return function() { 11 | if (timeout === false) { 12 | setTimeout(function() { 13 | func(); 14 | timeout = false; 15 | }, waitTime); 16 | timeout = true; 17 | } 18 | }; 19 | }; 20 | 21 | var closeToc = function() { 22 | $(".toc-wrapper").removeClass('open'); 23 | $("#nav-button").removeClass('open'); 24 | }; 25 | 26 | function loadToc($toc, tocLinkSelector, tocListSelector, scrollOffset) { 27 | var headerHeights = {}; 28 | var pageHeight = 0; 29 | var windowHeight = 0; 30 | var originalTitle = document.title; 31 | 32 | var recacheHeights = function() { 33 | headerHeights = {}; 34 | pageHeight = $(document).height(); 35 | windowHeight = $(window).height(); 36 | 37 | $toc.find(tocLinkSelector).each(function() { 38 | var targetId = $(this).attr('href'); 39 | if (targetId[0] === "#") { 40 | headerHeights[targetId] = $(targetId).offset().top; 41 | } 42 | }); 43 | }; 44 | 45 | var refreshToc = function() { 46 | var currentTop = $(document).scrollTop() + scrollOffset; 47 | 48 | if (currentTop + windowHeight >= pageHeight) { 49 | // at bottom of page, so just select last header by making currentTop very large 50 | // this fixes the problem where the last header won't ever show as active if its content 51 | // is shorter than the window height 52 | currentTop = pageHeight + 1000; 53 | } 54 | 55 | var best = null; 56 | for (var name in headerHeights) { 57 | if ((headerHeights[name] < currentTop && headerHeights[name] > headerHeights[best]) || best === null) { 58 | best = name; 59 | } 60 | } 61 | 62 | // Catch the initial load case 63 | if (currentTop == scrollOffset && !loaded) { 64 | best = window.location.hash; 65 | loaded = true; 66 | } 67 | 68 | var $best = $toc.find("[href='" + best + "']").first(); 69 | if (!$best.hasClass("active")) { 70 | // .active is applied to the ToC link we're currently on, and its parent