├── .gitignore ├── SublimeMonoKai.jar ├── AppCode ├── SublimeMonokai.jar └── buildjar-AppCode.sh ├── code-samples ├── screenshots │ ├── screen1.png │ ├── screen2.png │ ├── screen3.png │ ├── screen4.png │ └── screen5.png ├── bash.sh ├── rust.rs ├── typescript.ts ├── cfml.cfm ├── kotlin.kt ├── html.html ├── go.go ├── groovy.groovy ├── gradle.gradle ├── ruby.rb ├── css.css ├── less.less ├── javascript.js ├── clojure.clj ├── python.py ├── latex.tex ├── cpp.cpp └── coffee.coffee └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | .idea -------------------------------------------------------------------------------- /SublimeMonoKai.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x-2a/Intellij-Colors-Sublime-Monokai/HEAD/SublimeMonoKai.jar -------------------------------------------------------------------------------- /AppCode/SublimeMonokai.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x-2a/Intellij-Colors-Sublime-Monokai/HEAD/AppCode/SublimeMonokai.jar -------------------------------------------------------------------------------- /code-samples/screenshots/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x-2a/Intellij-Colors-Sublime-Monokai/HEAD/code-samples/screenshots/screen1.png -------------------------------------------------------------------------------- /code-samples/screenshots/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x-2a/Intellij-Colors-Sublime-Monokai/HEAD/code-samples/screenshots/screen2.png -------------------------------------------------------------------------------- /code-samples/screenshots/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x-2a/Intellij-Colors-Sublime-Monokai/HEAD/code-samples/screenshots/screen3.png -------------------------------------------------------------------------------- /code-samples/screenshots/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x-2a/Intellij-Colors-Sublime-Monokai/HEAD/code-samples/screenshots/screen4.png -------------------------------------------------------------------------------- /code-samples/screenshots/screen5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x-2a/Intellij-Colors-Sublime-Monokai/HEAD/code-samples/screenshots/screen5.png -------------------------------------------------------------------------------- /AppCode/buildjar-AppCode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir colors 4 | cp SublimeMonokai.xml colors 5 | touch IntelliJ\ IDEA\ Global\ Settings 6 | 7 | jar cfM SublimeMonoKai.jar IntelliJ\ IDEA\ Global\ Settings colors 8 | 9 | rm -r colors 10 | rm IntelliJ\ IDEA\ Global\ Settings -------------------------------------------------------------------------------- /code-samples/bash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # SCRIPT: armstrongnumber.sh 3 | # USAGE: armstrongnumber.sh 4 | # PURPOSE: Check if the given number is Armstrong number ? 5 | # \\\\ //// 6 | # \\ - - // 7 | # @ @ 8 | # ---oOOo-( )-oOOo--- 9 | # Armstrong numbers are the sum of their own digits to the power of 10 | # the number of digits. As that is a slightly brief wording, let me 11 | # give an example: 12 | # 153 = 1³ + 5³ + 3³ 13 | # Each digit is raised to the power three because 153 has three 14 | # digits. They are totalled and we get the original number again! 15 | #Notice that Armstrong numbers are base dependent,but we'll mainly be 16 | # dealing with base 10 examples.The Armstrong numbers up to 5 digits 17 | # are 1 to 9,153, 370, 371, 407, 1634, 8208, 9474, 54748, 92727,93084 18 | # 19 | ##################################################################### 20 | # Script Starts Here # 21 | ##################################################################### 22 | 23 | echo -n "Enter the number: " 24 | read Number 25 | Length=${#Number} 26 | Sum=0 27 | OldNumber=$Number 28 | 29 | while [ $Number -ne 0 ] 30 | do 31 | Rem=$((Number%10)) 32 | Number=$((Number/10)) 33 | Power=$(echo "$Rem ^ $Length" | bc ) 34 | Sum=$((Sum+$Power)) 35 | done 36 | 37 | if [ $Sum -eq $OldNumber ] 38 | then 39 | echo "$OldNumber is an Armstrong number" 40 | else 41 | echo "$OldNumber is not an Armstrong number" 42 | fi -------------------------------------------------------------------------------- /code-samples/rust.rs: -------------------------------------------------------------------------------- 1 | // Sample Provided by @CleanCut 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | use std::collections::HashMap; 7 | use std::rc::Rc; 8 | 9 | mod stuff; 10 | 11 | pub enum Flag { 12 | Good, 13 | Bad, 14 | Ugly 15 | } 16 | 17 | pub trait Write { 18 | fn write(&mut self, buf: &[u8]) -> Result; 19 | } 20 | 21 | struct Object { 22 | flag: Flag, 23 | fields: HashMap 24 | } 25 | 26 | type RcObject = Rc>; 27 | 28 | impl Write for Object { 29 | fn write(&mut self, buf: &[u8]) -> Result { 30 | let s = stuff::write_map(&self.fields, buf)?; 31 | info!("{} byte(s) written", s); 32 | Ok(s) 33 | } 34 | } 35 | 36 | /* Block comment */ 37 | fn main() { 38 | // A simple integer calculator: 39 | // `+` or `-` means add or subtract by 1 40 | // `*` or `/` means multiply or divide by 2 41 | stuff::AppVersion::print(); 42 | 43 | let input = Option::None; 44 | let program = input.unwrap_or_else(|| "+ + * - /"); 45 | let mut accumulator = 0; 46 | 47 | for token in program.chars() { 48 | match token { 49 | '+' => accumulator += 1, 50 | '-' => accumulator -= 1, 51 | '*' => accumulator *= 2, 52 | '/' => accumulator /= 2, 53 | _ => { /* ignore everything else */ } 54 | } 55 | } 56 | 57 | info!("The program \"{}\" calculates the value {}", 58 | program, accumulator); 59 | } 60 | 61 | /// Some documentation `with code` 62 | /// # Heading 63 | /// [Rust](https://www.rust-lang.org/) 64 | #[cfg(target_os="linux")] 65 | unsafe fn a_function() { 66 | 'label: loop { 67 | println!("Hello\x20W\u{f3}rld!\u{abcdef}"); 68 | } 69 | } -------------------------------------------------------------------------------- /code-samples/typescript.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Sample from https://angular.io/guide/http 3 | */ 4 | 5 | @Injectable() 6 | export class CachingInterceptor implements HttpInterceptor { 7 | private HttpResponse: any; 8 | 9 | constructor(private cache: HttpCache) { 10 | } 11 | 12 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 13 | // Before doing anything, it's important to only cache GET requests. 14 | // Skip this interceptor if the request method isn't GET. 15 | if (req.method !== 'GET') { 16 | return next.handle(req); 17 | } 18 | 19 | // First, check the cache to see if this request exists. 20 | const cachedResponse = this.cache.get(req); 21 | if (cachedResponse) { 22 | // A cached response exists. Serve it instead of forwarding 23 | // the request to the next handler. 24 | return Observable.of(cachedResponse); 25 | } 26 | 27 | // No cached response exists. Go to the network, and cache 28 | // the response when it arrives. 29 | return next.handle(req).do(event => { 30 | // Remember, there may be other events besides just the response. 31 | if (event instanceof this.HttpResponse) { 32 | // Update the cache. 33 | this.cache.put(req, event); 34 | } 35 | }); 36 | } 37 | } 38 | 39 | function Injectable() { 40 | } 41 | 42 | interface HttpInterceptor { 43 | } 44 | 45 | interface HttpCache { 46 | get(req: HttpRequest): any; 47 | 48 | put(req: HttpRequest, event: any): any; 49 | } 50 | 51 | class HttpRequest { 52 | method: string; 53 | } 54 | 55 | class HttpHandler { 56 | handle(req: HttpRequest) { 57 | } 58 | } 59 | 60 | class Observable { 61 | static of(cachedResponse: any) { 62 | } 63 | } 64 | 65 | class HttpEvent { 66 | } -------------------------------------------------------------------------------- /code-samples/cfml.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | // Handle service initialization if necessary 6 | oService = createObject("component", "bugLog.components.service").init(instanceName = instance); 7 | oAppService = createObject("component", "bugLog.components.hq.appService").init(instanceName = instance); 8 | 9 | oConfig = oService.getConfig(); 10 | settings = oAppService.getDigestSettings(); 11 | 12 | oMailerService = createObject("component", "bugLog.components.MailerService").init(oConfig); 13 | 14 | adminEmail = oConfig.getSetting("general.adminEmail"); 15 | recipients = len(trim(settings.recipients)) ? trim(settings.recipients) : adminEmail; 16 | 17 | if (!settings.enabled) { 18 | writeOutput("Digest report is not enabled."); 19 | abort; 20 | } else 21 | if (!len(adminEmail) or !isValid("email", adminEmail)) { 22 | writeOutput("The Administrator Email setting must be a valid email address."); 23 | abort; 24 | } 25 | 26 | 27 | 28 | 29 | 30 |

31 | ** This email has been sent from the BugLogHQ server at 32 | #thisHost# 33 |

34 |
35 |
36 | 37 | 38 | 45 | Done. (email sent to #recipients#). 46 | 47 | Done. (email not sent). 48 | 49 | -------------------------------------------------------------------------------- /code-samples/kotlin.kt: -------------------------------------------------------------------------------- 1 | import java.util.ArrayDeque 2 | 3 | class BinaryTree(val data: T) { 4 | 5 | var leftTree: BinaryTree? = null 6 | var rightTree: BinaryTree? = null 7 | 8 | fun prefix(visitor: (T) -> Unit) { 9 | visitor(data) 10 | leftTree?.prefix(visitor) 11 | rightTree?.prefix(visitor) 12 | } 13 | 14 | fun infix(visitor: (T) -> Unit) { 15 | leftTree?.prefix(visitor) 16 | visitor(data) 17 | rightTree?.prefix(visitor) 18 | } 19 | 20 | fun postfix(visitor: (T) -> Unit) { 21 | leftTree?.prefix(visitor) 22 | rightTree?.prefix(visitor) 23 | visitor(data) 24 | } 25 | 26 | fun visitNodesAtLevel(visitor: (T) -> Unit, targetLevel: Int, currentLevel: Int = 0) { 27 | if (currentLevel == targetLevel) { 28 | visitor(data) 29 | } else { 30 | leftTree?.visitNodesAtLevel(visitor, targetLevel, currentLevel + 1) 31 | rightTree?.visitNodesAtLevel(visitor, targetLevel, currentLevel + 1) 32 | } 33 | } 34 | 35 | fun height(): Int { 36 | if (leftTree == null && rightTree == null) { 37 | return 0 38 | } else { 39 | val leftTreeHeight = leftTree?.height() ?: 0 40 | val rightTreeHeight = rightTree?.height() ?: 0 41 | return 1 + Math.max(leftTreeHeight, rightTreeHeight) 42 | } 43 | } 44 | 45 | fun bfs(visitor: (T) -> Unit) { 46 | val queue = ArrayDeque>() 47 | queue.addLast(this) 48 | 49 | while (!queue.isEmpty()) { 50 | val node = queue.pollFirst(); 51 | visitor(node.data) 52 | 53 | val left = node.leftTree 54 | if (left != null) { 55 | queue.addLast(left) 56 | } 57 | 58 | val right = node.rightTree 59 | if (right != null) { 60 | queue.addLast(right) 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /code-samples/html.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 |

Hello world! This is HTML5 Boilerplate.

31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /code-samples/go.go: -------------------------------------------------------------------------------- 1 | // _Slices_ are a key data type in Go, giving a more 2 | // powerful interface to sequences than arrays. 3 | 4 | package main 5 | 6 | import "fmt" 7 | 8 | type Params struct { 9 | a, b, c int 10 | } 11 | 12 | func doIt(p Params) int { 13 | return p.a + p.b + p.c 14 | } 15 | 16 | // you can call it without specifying all parameters 17 | 18 | func main() { 19 | doIt(Params{a: 1, c: 9}) 20 | 21 | // Unlike arrays, slices are typed only by the 22 | // elements they contain (not the number of elements). 23 | // To create an empty slice with non-zero length, use 24 | // the builtin `make`. Here we make a slice of 25 | // `string`s of length `3` (initially zero-valued). 26 | s := make([]string, 3) 27 | fmt.Println("emp:", s) 28 | 29 | // We can set and get just like with arrays. 30 | s[0] = "a" 31 | s[1] = "b" 32 | s[2] = "c" 33 | fmt.Println("set:", s) 34 | fmt.Println("get:", s[2]) 35 | 36 | // `len` returns the length of the slice as expected. 37 | fmt.Println("len:", len(s)) 38 | 39 | // In addition to these basic operations, slices 40 | // support several more that make them richer than 41 | // arrays. One is the builtin `append`, which 42 | // returns a slice containing one or more new values. 43 | // Note that we need to accept a return value from 44 | // append as we may get a new slice value. 45 | s = append(s, "d") 46 | s = append(s, "e", "f") 47 | fmt.Println("apd:", s) 48 | 49 | // Slices can also be `copy`'d. Here we create an 50 | // empty slice `c` of the same length as `s` and copy 51 | // into `c` from `s`. 52 | c := make([]string, len(s)) 53 | copy(c, s) 54 | fmt.Println("cpy:", c) 55 | 56 | // Slices support a "slice" operator with the syntax 57 | // `slice[low:high]`. For example, this gets a slice 58 | // of the elements `s[2]`, `s[3]`, and `s[4]`. 59 | l := s[2:5] 60 | fmt.Println("sl1:", l) 61 | 62 | // This slices up to (but excluding) `s[5]`. 63 | l = s[:5] 64 | fmt.Println("sl2:", l) 65 | 66 | // And this slices up from (and including) `s[2]`. 67 | l = s[2:] 68 | fmt.Println("sl3:", l) 69 | 70 | // We can declare and initialize a variable for slice 71 | // in a single line as well. 72 | t := []string{"g", "h", "i"} 73 | fmt.Println("dcl:", t) 74 | 75 | // Slices can be composed into multi-dimensional data 76 | // structures. The length of the inner slices can 77 | // vary, unlike with multi-dimensional arrays. 78 | twoD := make([][]int, 3) 79 | for i := 0; i < 3; i++ { 80 | innerLen := i + 1 81 | twoD[i] = make([]int, innerLen) 82 | for j := 0; j < innerLen; j++ { 83 | twoD[i][j] = i+j 84 | } 85 | } 86 | fmt.Println("2d: ", twoD) 87 | } 88 | -------------------------------------------------------------------------------- /code-samples/groovy.groovy: -------------------------------------------------------------------------------- 1 | File jsDoc = new File("asdf.js") 2 | jsDoc.delete() 3 | 4 | //Write the intro to index document 5 | jsDoc << '// This high-level directory exposes the internal structure \n' 6 | 7 | doccoClosure = { 8 | //Filter out svn and lib folders 9 | if (!it.canonicalPath.toString().contains(".svn") && !it.canonicalPath.toString().contains("lib")) { 10 | 11 | //recurse through each directory 12 | it.eachDir(doccoClosure); 13 | 14 | strTemp = it.canonicalPath.toString() 15 | 16 | //Add the directory to index document 17 | jsDoc << "//# `${dirPath}` \n"; 18 | 19 | //Create folders for documentation 20 | newDirName = dirPath.substring(dirPath.lastIndexOf("/") + 1, dirPath.length()) 21 | new File("${newDirName}").mkdir() 22 | 23 | it.eachFile { 24 | //Only js files, not json 25 | if (it.canonicalPath.toString().contains(".js") && !it.canonicalPath.toString().contains(".json")) { 26 | //Get the path/filename 27 | strTempB = it.canonicalPath.toString(); 28 | jsFullName = strTempB.substring(strTempB.lastIndexOf("/") + 1, strTempB.length()) 29 | jsName = jsFullName.substring(0,jsFullName.indexOf(".js")) 30 | 31 | //Document the file 32 | def command = "docco ${it.canonicalPath}" 33 | def proc = command.execute() 34 | proc.waitFor() 35 | 36 | //Setup docco structure around the file 37 | File jsFile = new File("docs/${jsName}.html") 38 | File dir = new File("${newDirName}"); 39 | File doccoCSSFile = new File("docs/docco.css") 40 | 41 | //Make docco structure specific 42 | jsFile.renameTo(new File(dir,jsFile.getName())); 43 | doccoCSSFile.renameTo(new File(dir,doccoCSSFile.getName())); 44 | 45 | println("Documenting ${jsFullName} to lb-docs/${newDirName}/${jsFile.getName()}") 46 | 47 | //Add the document and path to the index document 48 | jsDoc << "// [${jsFullName}](${newDirName}\\${jsFile.getName()}) \n" 49 | 50 | } 51 | } 52 | } 53 | } 54 | 55 | 56 | def procC = commandC.execute() 57 | procC.waitFor() 58 | 59 | //Wrap with docco structure 60 | File doccoCSSFile = new File("docs/docco.css") 61 | File dir = new File("."); 62 | jsFile.renameTo(new File(dir,jsFile.getName())); 63 | doccoCSSFile.renameTo(new File(dir,doccoCSSFile.getName())); 64 | 65 | //Open index and done. 66 | def procFinal = commandFinal.execute() 67 | procFinal.waitFor() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Intellij Colors: Sublime Monokai Theme v8.7.2 2 | ========================================= 3 | 4 | Dark color theme for Intellij inspired by Sublime Text's Monokai Theme. Also works in other Jetbrains products including Webstorm, PyCharm, GoLand, RubyMine, and PhPStorm. 5 | 6 | To install 7 | - Download or build the jar (buildJar.sh) 8 | - Intellij -> File -> Import Settings -> Select Jar (Or icls -> SublimeMonokai.xml) 9 | - Select Sublime Monokai as the theme in Settings -> Editor -> Colors & Fonts 10 | - Note: If you can't see "Import Settings" anymore, temporarily disable you Jetbrains Settings Sync(File -> Disable Settings Sync) 11 | 12 | Special thanks to collaborators: 13 | - [murtaza52](https://github.com/murtaza52) for clojure support 14 | - [DecKen](https://github.com/DecKen) and [Sven Bendel](https://github.com/ubuntudroid) for carat identifiers 15 | - [gombosg](https://github.com/gombosg) for helping maintain java 16 | - [CleanCut](https://github.com/CleanCut) for rust support 17 | 18 | Supported Languages: 19 | 20 | Java (IJ) 21 | Golang (IJ, Goland) 22 | Python 2 and 3 (IJ, PyCharm) 23 | JavaScript (IJ, Webstorm) 24 | TypeScript (IJ, Webstorm) 25 | CSS/SASS/SCSSS (IJ, Webstorm) 26 | C++ (CLion) 27 | Rust (IJ, Clion) 28 | Bash (tranisitioning to 2019 plugin) 29 | Markdown 30 | HTML 31 | JSON 32 | JSP 33 | LESS 34 | SQL 35 | XML 36 | Diff 37 | YAML 38 | LaTeX (< 2018) 39 | Kotlin (< 2018, need maintainer) 40 | CFML (< 2015, need maintainer) 41 | Clojure (< 2015, need maintainer) 42 | CoffeScript (< 2015, need maintainer) 43 | Gradle (< 2015, need maintainer) 44 | Groovy (< 2015, need maintainer) 45 | Puppet (< 2015, need maintainer) 46 | Handlebars/Mustache (< 2015, need maintainer) 47 | Haskell (< 2015, need maintainer) 48 | Jade (< 2015, need maintainer) 49 | PHP (phpstorm < 2015, need maintainer) 50 | Scala (< 2015, need maintainer) 51 | Ruby (< 2015, need maintainer) 52 | 53 | Objective C (< 2015, Use Appcode/SublimeMonokai.jar) 54 | 55 | Not supported (no plugin support): 56 | 57 | Flex/Actionscript/MXML 58 | AppleScript 59 | 60 | To request support for a language, please fork, create a code sample in the code-samples folder, then create a pull request. I will review it and create a scheme. Feel free to PR your own version for a language. 61 | 62 | ## Screenshots 63 | 64 | ![](https://github.com/y3sh/Intellij-Colors-Sublime-Monokai/raw/master/code-samples/screenshots/screen1.png) 65 | 66 | ![](https://github.com/y3sh/Intellij-Colors-Sublime-Monokai/raw/master/code-samples/screenshots/screen2.png) 67 | 68 | ![](https://github.com/y3sh/Intellij-Colors-Sublime-Monokai/raw/master/code-samples/screenshots/screen3.png) 69 | 70 | ![](https://github.com/y3sh/Intellij-Colors-Sublime-Monokai/raw/master/code-samples/screenshots/screen4.png) 71 | 72 | ![](https://github.com/y3sh/Intellij-Colors-Sublime-Monokai/raw/master/code-samples/screenshots/screen5.png) 73 | 74 | -------------------------------------------------------------------------------- /code-samples/gradle.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'jetty' 2 | apply plugin: 'war' 3 | 4 | ext.manifest = true 5 | 6 | buildscript { 7 | repositories { 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'org.gradle.api.plugins:gradle-tomcat-plugin:0.9.9' 13 | } 14 | } 15 | 16 | jettyRun { 17 | httpPort = 8080 18 | stopPort = 8081 19 | } 20 | 21 | tomcatRun { 22 | httpPort = 8080 23 | httpsPort = 8081 24 | enableSSL = true 25 | } 26 | 27 | war { 28 | baseName = 'pointdrive' 29 | } 30 | 31 | configurations.all { 32 | resolutionStrategy { 33 | force 'org.apache.httpcomponents:httpclient:4.2.2' 34 | } 35 | } 36 | 37 | dependencies { 38 | def tomcatVersion = '7.0.42' 39 | 40 | //********************************************************** 41 | // Actual compile dependencies 42 | //********************************************************** 43 | compile("org.springframework:spring-web:3.2.1.RELEASE") { 44 | exclude module: 'commons-logging' 45 | } 46 | compile("org.springframework:spring-webmvc:3.2.1.RELEASE") { 47 | exclude module: 'commons-logging' 48 | } 49 | compile 'javax.mail:mail:1.4.7' 50 | 51 | // API documentation 52 | compile ("com.knappsack:swagger4spring-web:0.3.3") { 53 | exclude group: 'ch.qos.logback' 54 | } 55 | providedCompile "javax.servlet:javax.servlet-api:3.0.1" 56 | 57 | //********************************************************** 58 | // Runtime only dependencies (included so that the WAR task 59 | // includes in the WAR file) 60 | //********************************************************** 61 | runtime "log4j:log4j:1.2.17" 62 | runtime "log4j:apache-log4j-extras:1.2.17" 63 | runtime "org.slf4j:slf4j-log4j12:1.7.2" 64 | runtime "org.slf4j:jcl-over-slf4j:1.7.2" 65 | 66 | runtime "com.fasterxml.jackson.datatype:jackson-datatype-joda:2.1.0" 67 | 68 | runtime "org.springframework.security:spring-security-config:3.1.3.RELEASE" 69 | runtime "org.springframework.security:spring-security-taglibs:3.1.3.RELEASE" 70 | 71 | runtime "javax.servlet:jstl:1.2" 72 | 73 | // tomcat7 74 | tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}", 75 | "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}" 76 | tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") { 77 | exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj' 78 | } 79 | 80 | runtime ':carrot-log4j-aws-sns:1.1.3-SNAPSHOT' 81 | 82 | runtime 'org.apache.httpcomponents:fluent-hc:4.2.2' 83 | runtime 'org.apache.httpcomponents:httpclient:4.2.2' 84 | runtime 'org.apache.httpcomponents:httpclient-cache:4.2.2' 85 | runtime 'com.googlecode.json-simple:json-simple:1.1.1' 86 | 87 | //********************************************************** 88 | runtime 'org.microemu:microemulator:2.0.4' 89 | runtime("com.thetransactioncompany:cors-filter:1.3.2") { 90 | exclude module: "servlet-api" 91 | } 92 | runtime "cglib:cglib:2.2.2" 93 | 94 | 95 | //********************************************************** 96 | // Test Dependencies 97 | //********************************************************** 98 | testCompile "org.springframework:spring-test:3.1.3.RELEASE" 99 | testCompile "org.springframework:spring-test-mvc:1.0.0.M2" 100 | 101 | } 102 | -------------------------------------------------------------------------------- /code-samples/ruby.rb: -------------------------------------------------------------------------------- 1 | # Author: Hiroshi Ichikawa 2 | # The license of this source is "New BSD Licence" 3 | 4 | require "cgi" 5 | 6 | require "google/api_client" 7 | 8 | 9 | module GoogleDrive 10 | 11 | module Util #:nodoc: 12 | 13 | EXT_TO_CONTENT_TYPE = { 14 | '.csv' => "text/csv", 15 | '.tsv' => "text/tab-separated-values", 16 | '.tab' => "text/tab-separated-values", 17 | ".doc" => "application/msword", 18 | ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", 19 | ".ods" => "application/x-vnd.oasis.opendocument.spreadsheet", 20 | ".odt" => "application/vnd.oasis.opendocument.text", 21 | ".rtf" => "application/rtf", 22 | ".sxw" => "application/vnd.sun.xml.writer", 23 | ".txt" => "text/plain", 24 | ".xls" => "application/vnd.ms-excel", 25 | ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 26 | ".pdf" => "application/pdf", 27 | ".png" => "image/png", 28 | ".ppt" => "application/vnd.ms-powerpoint", 29 | ".pps" => "application/vnd.ms-powerpoint", 30 | ".htm" => "text/html", 31 | ".html" => "text/html", 32 | ".zip" => "application/zip", 33 | ".swf" => "application/x-shockwave-flash", 34 | } 35 | 36 | module_function 37 | 38 | def encode_query(params) 39 | return params.map() { |k, v| CGI.escape(k.to_s()) + "=" + CGI.escape(v.to_s()) }.join("&") 40 | end 41 | 42 | def concat_url(url, piece) 43 | (url_base, url_query) = url.split(/\?/, 2) 44 | (piece_base, piece_query) = piece.split(/\?/, 2) 45 | result_query = [url_query, piece_query].select() { |s| s && !s.empty? }.join("&") 46 | return (url_base || "") + 47 | (piece_base || "") + 48 | (result_query.empty? ? "" : "?#{result_query}") 49 | end 50 | 51 | def h(str) 52 | # Should also escape "\n" to keep it in cell contents. 53 | return CGI.escapeHTML(str.to_s()).gsub(/\n/, ' ') 54 | end 55 | 56 | def construct_query(arg) 57 | 58 | case arg 59 | 60 | when String 61 | return arg 62 | 63 | when Array 64 | if arg[0].scan(/\?/).size != arg.size - 1 65 | raise( 66 | ArgumentError, 67 | "The number of placeholders doesn't match the number of arguments: %p" % [arg]) 68 | end 69 | i = 1 70 | return arg[0].gsub(/\?/) do 71 | v = arg[i] 72 | i += 1 73 | case v 74 | when String 75 | "'%s'" % v.gsub(/['\\]/) { "\\" + $& } 76 | when Time 77 | "'%s'" % v.iso8601 78 | when TrueClass 79 | "true" 80 | when FalseClass 81 | "false" 82 | else 83 | raise(ArgumentError, "Expected String, Time, true or false, but got %p" % [v]) 84 | end 85 | end 86 | 87 | else 88 | raise(ArgumentError, "Expected String or Array, but got %p" % [arg]) 89 | 90 | end 91 | 92 | end 93 | 94 | def construct_and_query(args) 95 | return args.select() { |a| a }.map() { |a| "(%s)" % construct_query(a) }.join(" and ") 96 | end 97 | 98 | def convert_params(params) 99 | str_params = {} 100 | for k, v in params 101 | str_params[k.to_s()] = v 102 | end 103 | old_terms = [] 104 | new_params = {} 105 | for k, v in str_params 106 | case k 107 | when "q" 108 | new_params["q"] = construct_query(v) 109 | # Parameters in the old API. 110 | when "title" 111 | if str_params["title-exact"].to_s() == "true" 112 | old_terms.push(["title = ?", v]) 113 | else 114 | old_terms.push(["title contains ?", v]) 115 | end 116 | when "title-exact" 117 | # Skips it. It is handled above. 118 | when "opened-min" 119 | old_terms.push(["lastViewedByMeDate >= ?", v]) 120 | when "opened-max" 121 | old_terms.push(["lastViewedByMeDate <= ?", v]) 122 | when "edited-min" 123 | old_terms.push(["modifiedDate >= ?", v]) 124 | when "edited-max" 125 | old_terms.push(["modifiedDate <= ?", v]) 126 | when "owner" 127 | old_terms.push(["? in owners", v]) 128 | when "writer" 129 | old_terms.push(["? in writers", v]) 130 | when "reader" 131 | old_terms.push(["? in readers", v]) 132 | when "showfolders" 133 | if v.to_s() == "false" 134 | old_terms.push("mimeType != 'application/vnd.google-apps.folder'") 135 | end 136 | when "showdeleted" 137 | if v.to_s() == "false" 138 | old_terms.push("trashed = false") 139 | end 140 | when "ocr", "targetLanguage", "sourceLanguage" 141 | raise(ArgumentError, "'%s' parameter is no longer supported." % k) 142 | else 143 | new_params[k] = v 144 | end 145 | end 146 | if !old_terms.empty? 147 | if new_params.has_key?("q") 148 | raise(ArgumentError, "Cannot specify both 'q' parameter and old query parameters.") 149 | else 150 | new_params["q"] = construct_and_query(old_terms) 151 | end 152 | end 153 | return new_params 154 | end 155 | 156 | def singleton_class(obj) 157 | class << obj 158 | return self 159 | end 160 | end 161 | 162 | def delegate_api_methods(obj, api_obj, exceptions = []) 163 | sc = singleton_class(obj) 164 | names = api_obj.class.keys.keys - exceptions 165 | names.each() do |name| 166 | sc.__send__(:define_method, name) do 167 | api_obj.__send__(name) 168 | end 169 | end 170 | end 171 | 172 | def new_upload_io(path_or_io, params) 173 | content_type = 174 | params[:content_type] || 175 | (params[:file_name] ? EXT_TO_CONTENT_TYPE[::File.extname(params[:file_name]).downcase] : nil) || 176 | "application/octet-stream" 177 | return Google::APIClient::UploadIO.new(path_or_io, content_type) 178 | end 179 | 180 | end 181 | 182 | end -------------------------------------------------------------------------------- /code-samples/css.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #000000 url('../images/bg1.jpg'); 3 | padding: 35px 0px 35px 0px; 4 | } 5 | 6 | body, input { 7 | font-size: 12pt; 8 | font-family: "cambria", "times new roman", serif; 9 | color: #333333; 10 | } 11 | 12 | p { 13 | line-height: 1.5em; 14 | margin-bottom: 1.0em; 15 | text-align: justify; 16 | } 17 | 18 | a { 19 | color: #B96D00; 20 | text-decoration: underline; 21 | } 22 | 23 | a:hover { 24 | text-decoration: none; 25 | } 26 | 27 | h3 span { 28 | font-weight: normal; 29 | } 30 | 31 | h3, h4 { 32 | display: inline; 33 | font-weight: bold; 34 | background-repeat: no-repeat; 35 | background-position: right; 36 | } 37 | 38 | h3 { 39 | font-size: 1.7em; 40 | padding-right: 34px; 41 | background-image: url('../images/db1.gif'); 42 | } 43 | 44 | h4 { 45 | font-size: 1.2em; 46 | padding-right: 28px; 47 | background-image: url('../images/db2.gif'); 48 | } 49 | 50 | .contentarea { 51 | padding-top: 1.3em; 52 | } 53 | 54 | img { 55 | border: solid 1px #6F5230; 56 | } 57 | 58 | img.left { 59 | position: relative; 60 | float: left; 61 | margin: 0em 1.8em 1.4em 0em; 62 | } 63 | 64 | img.right { 65 | position: relative; 66 | float: right; 67 | margin: 0em 0em 1.8em 1.8em; 68 | } 69 | 70 | .divider1 { 71 | position: relative; 72 | background: #fff url('../images/border2.gif') repeat-x; 73 | height: 14px; 74 | margin: 2.0em 0em 1.5em 0em; 75 | clear: both; 76 | } 77 | 78 | .divider2 { 79 | position: relative; 80 | height: 1px; 81 | border-bottom: solid 1px #eaeaea; 82 | margin: 2.0em 0em 2.0em 0em; 83 | } 84 | 85 | .post .details { 86 | position: relative; 87 | top: -1.5em; 88 | font-size: 0.8em; 89 | color: #787878; 90 | } 91 | 92 | .post ul.controls { 93 | clear: both; 94 | } 95 | 96 | .post ul.controls li { 97 | display: inline; 98 | font-size: 0.8em; 99 | } 100 | 101 | .post ul.controls li a { 102 | background-repeat: no-repeat; 103 | background-position: left; 104 | padding: 0em 1.0em 0em 20px; 105 | } 106 | 107 | .post ul.controls li a.printerfriendly { 108 | background-image: url('../images/icon-printerfriendly.gif'); 109 | } 110 | 111 | .post ul.controls li a.comments { 112 | background-image: url('../images/icon-comments.gif'); 113 | } 114 | 115 | .post ul.controls li a.more { 116 | background-image: url('../images/icon-more.gif'); 117 | } 118 | 119 | .box { 120 | position: relative; 121 | background: #FDFCF6 url('../images/boxbg.gif') repeat-x; 122 | left: -1.5em; 123 | top: -1.5em; 124 | padding: 1.5em; 125 | border-bottom: solid 1px #E1D2BD; 126 | margin-bottom: 1.0em; 127 | } 128 | 129 | ul.linklist { 130 | list-style: none; 131 | } 132 | 133 | ul.linklist li { 134 | line-height: 2.0em; 135 | } 136 | 137 | #upbg { 138 | position: absolute; 139 | top: 0px; 140 | left: 0px; 141 | width: 100%; 142 | height: 275px; 143 | background: #fff url('../images/bg2.jpg') repeat-x; 144 | z-index: 1; 145 | } 146 | 147 | #outer { 148 | z-index: 2; 149 | position: relative; 150 | /* 151 | The width value below controls the overall width of the design. By default it's set to 82% 152 | (so it'll take up 82% of the browser window's width). You can set it to a different percentage 153 | value (70%, 90%, etc.) or even a pixel value (760px, 800px, etc.) to enforce a fixed width. 154 | */ 155 | width: 82%; 156 | border: solid 7px #fff; 157 | background-color: #fff; 158 | margin: 0 auto; 159 | } 160 | 161 | #header { 162 | position: relative; 163 | width: 100%; 164 | height: 9.0em; 165 | background: #2B2B2B url('../images/topbg.gif') repeat-x; 166 | margin-bottom: 2px; 167 | } 168 | 169 | #headercontent { 170 | position: absolute; 171 | bottom: 0em; 172 | padding: 0em 2.0em 1.3em 2.0em; 173 | } 174 | 175 | #headercontent h1 { 176 | font-weight: normal; 177 | color: #fff; 178 | font-size: 2.5em; 179 | } 180 | 181 | #headercontent h1 sup { 182 | color: #777; 183 | } 184 | 185 | #headercontent h2 { 186 | font-size: 1.0em; 187 | font-weight: normal; 188 | color: #aaa; 189 | } 190 | 191 | #search { 192 | position: absolute; 193 | top: 5.5em; 194 | right: 2.0em; 195 | padding-right: 0.0em; 196 | } 197 | 198 | #search input.text { 199 | margin-right: 0.5em; 200 | vertical-align: middle; 201 | border-top: solid 1px #000000; 202 | border-right: 0px; 203 | border-bottom: solid 1px #777777; 204 | border-left: 0px; 205 | padding: 0.15em; 206 | width: 10.0em; 207 | } 208 | 209 | #search input.submit { 210 | background: #939B00 url('../images/buttonbg.gif') repeat-x; 211 | border: solid 1px #ffffff; 212 | font-weight: bold; 213 | padding: 0.25em; 214 | font-size: 0.8em; 215 | color: #F2F3DE; 216 | vertical-align: middle; 217 | } 218 | 219 | #headerpic { 220 | position: relative; 221 | height: 109px; 222 | background: #fff url('../images/hdrpic.jpg') no-repeat top left; 223 | margin-bottom: 2px; 224 | } 225 | 226 | #menu { 227 | position: relative; 228 | background: #C29774 url('../images/menubg.gif') repeat-x top left; 229 | height: 3.5em; 230 | padding: 0em 1.0em 0em 1.0em; 231 | margin-bottom: 2px; 232 | } 233 | 234 | #menu ul { 235 | position: absolute; 236 | top: 1.1em; 237 | } 238 | 239 | #menu ul li { 240 | position: relative; 241 | display: inline; 242 | } 243 | 244 | #menu ul li a { 245 | padding: 0.5em 1.0em 0.9em 1.0em; 246 | color: #fff; 247 | text-decoration: none; 248 | } 249 | 250 | #menu ul li a:hover { 251 | text-decoration: underline; 252 | } 253 | 254 | #menu ul li a.active { 255 | background: #C29774 url('../images/menuactive.gif') repeat-x top left; 256 | } 257 | 258 | #menubottom { 259 | background: #fff url('../images/border1.gif') repeat-x; 260 | height: 14px; 261 | margin-bottom: 1.5em; 262 | } 263 | 264 | #content { 265 | padding: 0em 2.0em 0em 2.0em; 266 | } 267 | 268 | #primarycontainer { 269 | float: left; 270 | margin-right: -18.0em; 271 | width: 100%; 272 | } 273 | 274 | #primarycontent { 275 | margin: 1.5em 22.0em 0em 0em; 276 | } 277 | 278 | #secondarycontent { 279 | margin-top: 1.5em; 280 | float: right; 281 | width: 18.0em; 282 | } 283 | 284 | #footer { 285 | position: relative; 286 | height: 2.0em; 287 | clear: both; 288 | padding-top: 5.0em; 289 | background: #fff url('../images/border2.gif') repeat-x 0em 2.5em; 290 | font-size: 0.8em; 291 | } 292 | 293 | #footer .left { 294 | position: absolute; 295 | left: 2.0em; 296 | bottom: 1.2em; 297 | } 298 | 299 | #footer .right { 300 | position: absolute; 301 | right: 2.0em; 302 | bottom: 1.2em; 303 | } 304 | -------------------------------------------------------------------------------- /code-samples/less.less: -------------------------------------------------------------------------------- 1 | /* 2 | blue: #02D3B8 3 | tan: #F4926B 4 | */ 5 | 6 | body { 7 | background-color: #f7f7f7; 8 | color: #3e3e3e; 9 | .text-muted { 10 | color: #787878; 11 | } 12 | 13 | font-family: "Tempus-Sans"; 14 | } 15 | 16 | h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { 17 | font-family: "Tempus-Sans"; 18 | } 19 | 20 | a, p, dl, dd, dt { 21 | font-weight: bolder; 22 | } 23 | 24 | @font-face { 25 | font-family: "Tempus-Sans"; 26 | src: url('../fonts/TempusSans.TTF'), url('../fonts/TempusSans.eot'); /* IE */ 27 | } 28 | 29 | @font-face { 30 | font-family: "Dense-Regular"; 31 | src: url('../fonts/dense-regular.ttf'), url('../fonts/dense-regular.eot'); /* IE */ 32 | } 33 | 34 | .careers, .about { 35 | a, p, dl, dd, dt, span, h2 { 36 | font-family: Helvetica Neue, Arial; 37 | } 38 | 39 | &.about p { 40 | font-weight: normal; 41 | } 42 | 43 | &.about h2 { 44 | font-weight: 300; 45 | } 46 | } 47 | 48 | .menu { 49 | @media only screen and (max-width: 767px) { 50 | .menu-header { 51 | &:first-of-type { 52 | margin-top: .75em !important; 53 | } 54 | } 55 | } 56 | 57 | @media only screen and (max-width: 400px) { 58 | .hidden-400 { 59 | display: none; 60 | } 61 | 62 | .visible-400 { 63 | display: inherit; 64 | } 65 | } 66 | @media only screen and (min-width: 401px) { 67 | .hidden-400 { 68 | display: inherit; 69 | } 70 | 71 | .visible-400 { 72 | display: none; 73 | } 74 | } 75 | 76 | max-width: 56em; 77 | text-align: center; 78 | 79 | h4, h5 { 80 | &, span { 81 | font-family: Cambria, Georgia, serif; 82 | } 83 | } 84 | 85 | .menu-header { 86 | &:first-of-type { 87 | margin-top: 0; 88 | } 89 | 90 | margin-top: .5em; 91 | font-family: Dense-Regular; 92 | font-size: 350%; 93 | color: black; 94 | } 95 | 96 | .menu-section-header { 97 | margin-top: 2em; 98 | margin-bottom: 1.5em; 99 | 100 | &.more-margin { 101 | margin-top: 3em; 102 | } 103 | 104 | h3 { 105 | width: 100%; 106 | border-bottom: 1px solid #3E3E3E; 107 | line-height: 0.1em; 108 | margin: 10px 0 20px; 109 | 110 | span { 111 | background: #f7f7f7; 112 | padding: 0 10px; 113 | font-family: Dense-Regular; 114 | font-size: 160%; 115 | } 116 | } 117 | } 118 | 119 | .small-divider { 120 | width: 4em; 121 | border-top: 1px solid #3E3E3E; 122 | margin: 0 auto; 123 | } 124 | 125 | .menu-list { 126 | 127 | } 128 | 129 | h4.menu-item { 130 | font-weight: bolder; 131 | color: black; 132 | 133 | span.item-details { 134 | font-weight: normal; 135 | color: #3E3E3E; 136 | margin-left: 6px; 137 | margin-right: 6px; 138 | font-style: italic; 139 | font-size: 80%; 140 | } 141 | } 142 | 143 | h5.menu-item-options { 144 | font-style: italic; 145 | } 146 | } 147 | 148 | .home { 149 | p.lead { 150 | font-family: Helvetica Neue, Arial; 151 | } 152 | 153 | @media only screen and (min-width: 768px) { 154 | h2 { 155 | margin-left: -4px; 156 | } 157 | } 158 | @media only screen and (max-width: 767px) { 159 | h2 { 160 | margin-left: -1px; 161 | } 162 | } 163 | } 164 | 165 | .carousel .item { 166 | background-color: #f7f7f7; 167 | } 168 | 169 | .navbar-wrapper .container { 170 | @media only screen and (min-width: 768px) { 171 | width: 540px; 172 | } 173 | // @media only screen and (min-width : 768px) { width: 600px; } 174 | 175 | #nav-facebook { 176 | a { 177 | padding: 0; 178 | } 179 | img { 180 | margin-top: 12px; 181 | margin-left: 10px; 182 | margin-right: 10px; 183 | height: 25px; 184 | } 185 | } 186 | 187 | #nav-instagram { 188 | a { 189 | padding: 0; 190 | } 191 | img { 192 | margin-top: 12px; 193 | margin-left: 10px; 194 | height: 25px; 195 | } 196 | } 197 | 198 | .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { 199 | border-color: #cccccc; 200 | } 201 | 202 | .navbar.navbar-inverse.navbar-static-top { 203 | background-color: #d5d5d5; 204 | border: none; 205 | background-image: -webkit-linear-gradient(top, #fcfcfc 100%, #e4e4e4 0%); 206 | background-image: linear-gradient(to bottom, #fcfcfc 0%, #e4e4e4 100%); 207 | background-repeat: repeat-x; 208 | text-shadow: none; 209 | 210 | .navbar-nav .active a { 211 | background-image: -webkit-linear-gradient(top, #ff9c6c 0%, #F4926B 100%); 212 | background-image: linear-gradient(to bottom, #ff9c6c 0%, #F4926B 100%); 213 | background-repeat: repeat-x; 214 | -webkit-box-shadow: none; 215 | box-shadow: none; 216 | background-color: none; 217 | text-shadow: none; 218 | color: #FFF; 219 | } 220 | 221 | .navbar-nav li a { 222 | color: #0D0D0D; 223 | text-shadow: none; 224 | } 225 | 226 | .navbar-brand { 227 | color: #4aa7b3; 228 | text-shadow: none; 229 | padding: 0; 230 | img { 231 | height: 35px; 232 | } 233 | padding-top: 6px; 234 | padding-left: 26px; 235 | padding-right: 26px; 236 | } 237 | 238 | .navbar-toggle:hover, .navbar-toggle:focus { 239 | background-color: #d8815f; 240 | } 241 | .navbar-toggle { 242 | border-color: #d8815f; 243 | background-color: #f9946e; 244 | } 245 | 246 | } 247 | 248 | } 249 | 250 | .carousel-inner .item img { 251 | height: 1000px; 252 | max-width: none; 253 | &.reg-height { 254 | height: 500px; 255 | } 256 | 257 | &#resize-hot { 258 | height: 500px; 259 | } 260 | 261 | } 262 | 263 | .carousel-caption { 264 | right: 20%; 265 | left: 20%; 266 | padding-bottom: 155px; 267 | .capWrap { 268 | // background-color: rgba(248, 248, 248, 0.25); 269 | // padding:.25em; 270 | text-shadow: 0 2px 2px rgba(0, 0, 0, .8); 271 | -webkit-border-radius: 10px; 272 | -moz-border-radius: 10px; 273 | border-radius: 10px; 274 | width: 500px; 275 | margin: 0 auto; 276 | } 277 | } 278 | 279 | h2.featurette-heading { 280 | margin-top: 0; 281 | } 282 | 283 | .featurette-divider { 284 | margin-top: 3em; 285 | margin-bottom: 3em; 286 | } 287 | 288 | .btn-primary { 289 | background-image: -webkit-linear-gradient(top, #ff9d6b 0%, #F4926B 100%); 290 | background-image: linear-gradient(to bottom, #ff996f 0%, #F4926B 100%); 291 | background-repeat: repeat-x; 292 | border-color: #F4926B; 293 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); 294 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 295 | } 296 | 297 | .btn-primary:hover, 298 | .btn-primary:focus { 299 | background-color: #f9946e; 300 | background-position: 0 -15px; 301 | box-shadow: none; 302 | border-color: #d8815f; 303 | } 304 | 305 | .btn-primary:active, 306 | .btn-primary.active { 307 | background-color: #ec886d; 308 | border-color: #ec886d; 309 | box-shadow: none; 310 | } 311 | 312 | #mapIt { 313 | margin-left: 10px; 314 | margin-bottom: 6px; 315 | } 316 | 317 | #culturedBig { 318 | margin-top: 1em; 319 | margin-bottom: 1.5em; 320 | // max-width:90%; 321 | // @media only screen and (min-width : 768px) { width: 600px; } 322 | 323 | } 324 | 325 | footer { 326 | width: 100%; 327 | p { 328 | margin-top: 0; 329 | margin-bottom: 0; 330 | margin-right: 1em; 331 | } 332 | color: #949494; 333 | } 334 | 335 | .menu-image { 336 | height: 300px; 337 | } 338 | 339 | #myCarousel { 340 | margin-bottom: 30px; 341 | } -------------------------------------------------------------------------------- /code-samples/javascript.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 1.1.2 2 | 3 | // (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Backbone may be freely distributed under the MIT license. 5 | // For all details and documentation: 6 | // http://backbonejs.org 7 | 8 | (function(root, factory) { 9 | 10 | // Set up Backbone appropriately for the environment. Start with AMD. 11 | if (typeof define === 'function' && define.amd) { 12 | define(['underscore', 'jquery', 'exports'], function(_, $, exports) { 13 | // Export global even in AMD case in case this script is loaded with 14 | // others that may still expect a global Backbone. 15 | root.Backbone = factory(root, exports, _, $); 16 | }); 17 | 18 | // Next for Node.js or CommonJS. jQuery may not be needed as a module. 19 | } else if (typeof exports !== 'undefined') { 20 | var _ = require('underscore'); 21 | factory(root, exports, _); 22 | 23 | // Finally, as a browser global. 24 | } else { 25 | root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); 26 | } 27 | 28 | }(this, function(root, Backbone, _, $) { 29 | 30 | // Initial Setup 31 | // ------------- 32 | 33 | // Save the previous value of the `Backbone` variable, so that it can be 34 | // restored later on, if `noConflict` is used. 35 | var previousBackbone = root.Backbone; 36 | 37 | // Create local references to array methods we'll want to use later. 38 | var array = []; 39 | var push = array.push; 40 | var slice = array.slice; 41 | var splice = array.splice; 42 | 43 | // Current version of the library. Keep in sync with `package.json`. 44 | Backbone.VERSION = '1.1.2'; 45 | 46 | // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns 47 | // the `$` variable. 48 | Backbone.$ = $; 49 | 50 | // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable 51 | // to its previous owner. Returns a reference to this Backbone object. 52 | Backbone.noConflict = function() { 53 | root.Backbone = previousBackbone; 54 | return this; 55 | }; 56 | 57 | // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option 58 | // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and 59 | // set a `X-Http-Method-Override` header. 60 | Backbone.emulateHTTP = false; 61 | 62 | // Turn on `emulateJSON` to support legacy servers that can't deal with direct 63 | // `application/json` requests ... will encode the body as 64 | // `application/x-www-form-urlencoded` instead and will send the model in a 65 | // form param named `model`. 66 | Backbone.emulateJSON = false; 67 | 68 | // Backbone.Events 69 | // --------------- 70 | 71 | // A module that can be mixed in to *any object* in order to provide it with 72 | // custom events. You may bind with `on` or remove with `off` callback 73 | // functions to an event; `trigger`-ing an event fires all callbacks in 74 | // succession. 75 | // 76 | // var object = {}; 77 | // _.extend(object, Backbone.Events); 78 | // object.on('expand', function(){ alert('expanded'); }); 79 | // object.trigger('expand'); 80 | // 81 | var Events = Backbone.Events = { 82 | 83 | // Bind an event to a `callback` function. Passing `"all"` will bind 84 | // the callback to all events fired. 85 | on: function(name, callback, context) { 86 | if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; 87 | this._events || (this._events = {}); 88 | var events = this._events[name] || (this._events[name] = []); 89 | events.push({callback: callback, context: context, ctx: context || this}); 90 | return this; 91 | }, 92 | 93 | // Bind an event to only be triggered a single time. After the first time 94 | // the callback is invoked, it will be removed. 95 | once: function(name, callback, context) { 96 | if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; 97 | var self = this; 98 | var once = _.once(function() { 99 | self.off(name, once); 100 | callback.apply(this, arguments); 101 | }); 102 | once._callback = callback; 103 | return this.on(name, once, context); 104 | }, 105 | 106 | // Remove one or many callbacks. If `context` is null, removes all 107 | // callbacks with that function. If `callback` is null, removes all 108 | // callbacks for the event. If `name` is null, removes all bound 109 | // callbacks for all events. 110 | off: function(name, callback, context) { 111 | var retain, ev, events, names, i, l, j, k; 112 | if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; 113 | if (!name && !callback && !context) { 114 | this._events = void 0; 115 | return this; 116 | } 117 | names = name ? [name] : _.keys(this._events); 118 | for (i = 0, l = names.length; i < l; i++) { 119 | name = names[i]; 120 | if (events = this._events[name]) { 121 | this._events[name] = retain = []; 122 | if (callback || context) { 123 | for (j = 0, k = events.length; j < k; j++) { 124 | ev = events[j]; 125 | if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || 126 | (context && context !== ev.context)) { 127 | retain.push(ev); 128 | } 129 | } 130 | } 131 | if (!retain.length) delete this._events[name]; 132 | } 133 | } 134 | 135 | return this; 136 | }, 137 | 138 | // Trigger one or many events, firing all bound callbacks. Callbacks are 139 | // passed the same arguments as `trigger` is, apart from the event name 140 | // (unless you're listening on `"all"`, which will cause your callback to 141 | // receive the true name of the event as the first argument). 142 | trigger: function(name) { 143 | if (!this._events) return this; 144 | var args = slice.call(arguments, 1); 145 | if (!eventsApi(this, 'trigger', name, args)) return this; 146 | var events = this._events[name]; 147 | var allEvents = this._events.all; 148 | if (events) triggerEvents(events, args); 149 | if (allEvents) triggerEvents(allEvents, arguments); 150 | return this; 151 | }, 152 | 153 | // Tell this object to stop listening to either specific events ... or 154 | // to every object it's currently listening to. 155 | stopListening: function(obj, name, callback) { 156 | var listeningTo = this._listeningTo; 157 | if (!listeningTo) return this; 158 | var remove = !name && !callback; 159 | if (!callback && typeof name === 'object') callback = this; 160 | if (obj) (listeningTo = {})[obj._listenId] = obj; 161 | for (var id in listeningTo) { 162 | obj = listeningTo[id]; 163 | obj.off(name, callback, this); 164 | if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; 165 | } 166 | return this; 167 | } 168 | 169 | }; 170 | 171 | // Regular expression used to split event strings. 172 | var eventSplitter = /\s+/; 173 | 174 | // Implement fancy features of the Events API such as multiple event 175 | // names `"change blur"` and jQuery-style event maps `{change: action}` 176 | // in terms of the existing API. 177 | var eventsApi = function(obj, action, name, rest) { 178 | if (!name) return true; 179 | 180 | // Handle event maps. 181 | if (typeof name === 'object') { 182 | for (var key in name) { 183 | obj[action].apply(obj, [key, name[key]].concat(rest)); 184 | } 185 | return false; 186 | } 187 | 188 | // Handle space separated event names. 189 | if (eventSplitter.test(name)) { 190 | var names = name.split(eventSplitter); 191 | for (var i = 0, l = names.length; i < l; i++) { 192 | obj[action].apply(obj, [names[i]].concat(rest)); 193 | } 194 | return false; 195 | } 196 | 197 | return true; 198 | }; 199 | 200 | // A difficult-to-believe, but optimized internal dispatch function for 201 | // triggering events. Tries to keep the usual cases speedy (most internal 202 | // Backbone events have 3 arguments). 203 | var triggerEvents = function(events, args) { 204 | var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; 205 | switch (args.length) { 206 | case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; 207 | case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; 208 | case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; 209 | case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; 210 | default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; 211 | } 212 | }; 213 | 214 | var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; 215 | 216 | // Inversion-of-control versions of `on` and `once`. Tell *this* object to 217 | // listen to an event in another object ... keeping track of what it's 218 | // listening to. 219 | _.each(listenMethods, function(implementation, method) { 220 | Events[method] = function(obj, name, callback) { 221 | var listeningTo = this._listeningTo || (this._listeningTo = {}); 222 | var id = obj._listenId || (obj._listenId = _.uniqueId('l')); 223 | listeningTo[id] = obj; 224 | if (!callback && typeof name === 'object') callback = this; 225 | obj[implementation](name, callback, this); 226 | return this; 227 | }; 228 | }); 229 | 230 | // Aliases for backwards compatibility. 231 | Events.bind = Events.on; 232 | Events.unbind = Events.off; 233 | 234 | // Allow the `Backbone` object to serve as a global event bus, for folks who 235 | // want global "pubsub" in a convenient place. 236 | _.extend(Backbone, Events); 237 | 238 | // Backbone.Model 239 | // -------------- 240 | 241 | // Backbone **Models** are the basic data object in the framework -- 242 | // frequently representing a row in a table in a database on your server. 243 | // A discrete chunk of data and a bunch of useful, related methods for 244 | // performing computations and transformations on that data. 245 | 246 | // Create a new model with the specified attributes. A client id (`cid`) 247 | // is automatically generated and assigned for you. 248 | var Model = Backbone.Model = function(attributes, options) { 249 | var attrs = attributes || {}; 250 | options || (options = {}); 251 | this.cid = _.uniqueId('c'); 252 | this.attributes = {}; 253 | if (options.collection) this.collection = options.collection; 254 | if (options.parse) attrs = this.parse(attrs, options) || {}; 255 | attrs = _.defaults({}, attrs, _.result(this, 'defaults')); 256 | this.set(attrs, options); 257 | this.changed = {}; 258 | this.initialize.apply(this, arguments); 259 | }; 260 | 261 | // Attach all inheritable methods to the Model prototype. 262 | return Backbone; 263 | 264 | })); 265 | -------------------------------------------------------------------------------- /code-samples/clojure.clj: -------------------------------------------------------------------------------- 1 | ;;;; 2 | ;;;; Pong! 3 | ;;;; 4 | ;;;; Justin Grant 5 | ;;;; 2009/09/12 6 | 7 | (ns i27.games.pong 8 | (:import (java.awt Color Toolkit Font GraphicsEnvironment Graphics2D) 9 | (java.awt.image BufferStrategy) 10 | (java.awt.event ActionListener MouseMotionListener KeyListener 11 | MouseEvent KeyEvent) 12 | (javax.swing JFrame Timer))) 13 | 14 | (defstruct ball :h :w :x :y :sx :sy) 15 | (defn new-ball [& ; [h w x y sx sy]] (atom (struct ball h w x y sx sy))) 16 | (defn set-ball-size [b h w] (swap! b assoc :h h :w w)) 17 | (defn set-ball-speed [b sx sy] (swap! b assoc :sx sx :sy sy)) 18 | (defn set-ball-position [b x y] (swap! b assoc :x x :y y)) 19 | 20 | (defstruct paddle :h :w :x :y) 21 | (defn new-paddle [& ; [h w x y]] (atom (struct paddle h w x y))) 22 | (defn set-paddle-size [p h w] (swap! p assoc :h h :w w)) 23 | (defn set-paddle-position [p x y] (swap! p assoc :x x :y y)) 24 | 25 | (defstruct game :h :w :timer :score :started :my) 26 | (defn new-game [& ; [h w timer score started my]] 27 | (atom (struct game h w timer score started my))) 28 | (defn set-game-size [g h w] (swap! g assoc :h h :w w)) 29 | (defn set-game-timer [g t] (swap! g assoc :timer t)) 30 | (defn set-game-score [g s] (swap! g assoc :score s)) 31 | (defn set-game-mouse-y [g my] (swap! g assoc :my my)) 32 | (defn stop-game [g] 33 | (swap! g assoc :started false) (let [#^Timer t (@g :timer)] (.stop t))) 34 | (defn start-game [g] 35 | (swap! g assoc :started true) (let [#^Timer t (@g :timer)] (.start t))) 36 | (defn reset-game [g b p c] 37 | (set-ball-size b (* (@g :h) 0.0335) (* (@g :h) 0.0335)) 38 | (set-ball-position b 39 | (- (/ (@g :w) 2) (/ (@b :w) 2)) 40 | (- (/ (@g :h) 2) (/ (@b :h) 2))) 41 | (set-ball-speed b 15 15) 42 | (set-paddle-size p (* (@b :h) 5) (@b :w)) 43 | (set-paddle-position p 35 (- (/ (@g :h) 2) (/ (@p :h) 2))) 44 | (set-paddle-size c (@p :h) (@p :w)) 45 | (set-paddle-position c (- (@g :w) (@p :x) (@p :w)) (@p :y)) 46 | (set-game-score g 0)) 47 | 48 | (defn pong-frame [g b p c f1 f2] 49 | (proxy [JFrame ActionListener MouseMotionListener KeyListener] [] 50 | (paint [grf] 51 | (let [#^JFrame me this 52 | #^BufferStrategy bs (.getBufferStrategy me) 53 | #^Graphics2D gr (if (not= nil bs) (. bs getDrawGraphics) nil)] 54 | (if (not= nil gr) 55 | (do 56 | (.setColor gr Color/BLACK) 57 | (.fillRect gr 0 0 (@g :w) (@g :h)) 58 | (.setColor gr Color/WHITE) 59 | (.setFont gr f1) 60 | (.drawString gr (str "SCORE " (@g :score)) 20 20) 61 | (.fillRect gr (@p :x) (@p :y) (@p :w) (@p :h)) 62 | (.fillRect gr (@c :x) (@c :y) (@c :w) (@c :h)) 63 | (if (@g :started) 64 | (.fillRect gr (@b :x) (@b :y) (@b :w) (@b :h)) 65 | (do 66 | (.setFont gr f2) 67 | (.drawString gr "PONG!" 68 | (- (/ (@g :w) 2) 46) (- (/ (@g :h) 2) 16)) 69 | (.setFont gr f1) 70 | (.drawString gr "PRESS 'S' TO START, 'Q' TO QUIT" 71 | (- (/ (@g :w) 2) 200) (+ (/ (@g :h) 2) 30)))) 72 | (. gr dispose) 73 | (. bs show))))) 74 | 75 | (mouseMoved [#^MouseEvent e] 76 | (set-game-mouse-y g (.getY e)) 77 | (if (> ; (+ (@g :my) (/ (@p :h) 2)) (@g :h)) 78 | (set-game-mouse-y g (- (@g :h) (/ (@p :h) 2)))) 79 | (if (< ; (@g :my) (/ (@p :h) 2)) 80 | (set-game-mouse-y g (/ (@p :h) 2))) 81 | (set-paddle-position p (@p :x) (- (@g :my) (/ (@p :h) 2))) 82 | (let [#^JFrame me this] (.repaint me))) 83 | 84 | (mouseDragged [e]) 85 | 86 | (keyPressed [#^KeyEvent e] 87 | (when (and (not (@g :started)) (= (. e getKeyChar) s)) 88 | (reset-game g b p c) (start-game g)) 89 | (when (= (. e getKeyChar) q) (System/exit 0))) 90 | 91 | (keyReleased [e]) 92 | 93 | (keyTyped [e]) 94 | 95 | (actionPerformed [e] 96 | ;; update ball position 97 | (set-ball-position 98 | b (+ (@b :x) (@b :sx)) (+ (@b :y) (@b :sy))) 99 | ;; update ball y direction 100 | (when (or (= (+ (@b :y) (@b :h)) (@g :h))) 101 | (set-ball-speed b (@b :sx) (* -1 (@b :sy)))) 102 | ;; check if player returns ball 103 | (when (and (= (+ (@b :y) (@b :h)) (@p :y)) 104 | ((@b :x) (@p :x))) 105 | (set-ball-speed b (* -1 (@b :sx)) (@b :sy)) 106 | (set-game-score g (inc (@g :score))) 107 | (set-ball-speed b (+ 1 (@b :sx)) (@b :sy))) ; game gets faster 108 | ;; check when computer returns ball 109 | (when (and (> ;= (+ (@b :x) (@b :w)) (@c :x)) 110 | (> ;= (+ (@b :y) (@b :h)) (@c :y)) 111 | ((+ (@c :y) (/ (@p :h) 2)) (/ (@g :h) 2)) 112 | (set-paddle-position 113 | c (@c :x) (- (@c :y) (* -1 (@b :sx)))) 114 | (set-paddle-position 115 | c (@c :x) (+ (@c :y) (* -1 (@b :sx)))))) 116 | (if ((+ (@c :y) (@p :h)) (@g :h)) 117 | (set-paddle-position c (@c :x) (- (@g :h) (@p :h)))) 118 | ;; check game over 119 | (when (or (< ; (+ (@b :x) (@b :w)) 0) 120 | (> ; (+ (@b :x) (@b :w)) (@g :w))) 121 | (set-paddle-position p (@p :x) 122 | (- (/ (@g :h) 2) (/ (@p :h) 2))) 123 | (stop-game g)) 124 | (let [#^JFrame me this] 125 | (.repaint me))))) 126 | 127 | (defn -main [] 128 | (let [tk (. Toolkit getDefaultToolkit) 129 | ge (GraphicsEnvironment/getLocalGraphicsEnvironment) 130 | gd (. ge getDefaultScreenDevice) 131 | thegame (new-game (.. tk getScreenSize height) 132 | (.. tk getScreenSize width)) 133 | theball (new-ball) 134 | theplayer (new-paddle) 135 | thecomputer (new-paddle) 136 | #^JFrame screen (pong-frame 137 | thegame theball theplayer thecomputer 138 | (Font. "Courier New" Font/BOLD 24) 139 | (Font. "Courier New" Font/BOLD 44))] 140 | (set-game-timer thegame (Timer. 20 screen)) 141 | (if (not (. screen isDisplayable)) (. screen setUndecorated true)) 142 | (.setVisible screen true) 143 | (. (.getContentPane screen) setBackground Color/BLACK) 144 | (. (.getContentPane screen) setIgnoreRepaint true) 145 | (doto screen 146 | (.setResizable false) 147 | (.setBackground Color/BLACK) (.setIgnoreRepaint true) 148 | (.addMouseMotionListener screen) (.addKeyListener screen)) 149 | (. gd setFullScreenWindow screen) 150 | (. screen createBufferStrategy 2) 151 | (reset-game thegame theball theplayer thecomputer))) 152 | 153 | (-main) -------------------------------------------------------------------------------- /code-samples/python.py: -------------------------------------------------------------------------------- 1 | """ 2 | A classic (not left-leaning) Red-Black Tree implementation, supporting addition and deletion. 3 | """ 4 | 5 | # The possible Node colors 6 | BLACK = 'BLACK' 7 | RED = 'RED' 8 | NIL = 'NIL' 9 | 10 | 11 | class Node: 12 | def __init__(self, value, color, parent, left=None, right=None): 13 | self.value = value 14 | self.color = color 15 | self.parent = parent 16 | self.left = left 17 | self.right = right 18 | 19 | def __repr__(self): 20 | return '{color} {val} Node'.format(color=self.color, val=self.value) 21 | 22 | def __iter__(self): 23 | if self.left.color != NIL: 24 | yield from self.left.__iter__() 25 | 26 | yield self.value 27 | 28 | if self.right.color != NIL: 29 | yield from self.right.__iter__() 30 | 31 | def __eq__(self, other): 32 | if self.color == NIL and self.color == other.color: 33 | return True 34 | 35 | if self.parent is None or other.parent is None: 36 | parents_are_same = self.parent is None and other.parent is None 37 | else: 38 | parents_are_same = self.parent.value == other.parent.value and self.parent.color == other.parent.color 39 | return self.value == other.value and self.color == other.color and parents_are_same 40 | 41 | def has_children(self) -> bool: 42 | """ Returns a boolean indicating if the node has children """ 43 | return bool(self.get_children_count()) 44 | 45 | def get_children_count(self) -> int: 46 | """ Returns the number of NOT NIL children the node has """ 47 | if self.color == NIL: 48 | return 0 49 | return sum([int(self.left.color != NIL), int(self.right.color != NIL)]) 50 | 51 | 52 | class RedBlackTree: 53 | # every node has null nodes as children initially, create one such object for easy management 54 | NIL_LEAF = Node(value=None, color=NIL, parent=None) 55 | 56 | def __init__(self): 57 | self.count = 0 58 | self.root = None 59 | self.ROTATIONS = { 60 | # Used for deletion and uses the sibling's relationship with his parent as a guide to the rotation 61 | 'L': self._right_rotation, 62 | 'R': self._left_rotation 63 | } 64 | 65 | def __iter__(self): 66 | if not self.root: 67 | return list() 68 | yield from self.root.__iter__() 69 | 70 | def add(self, value): 71 | if not self.root: 72 | self.root = Node(value, color=BLACK, parent=None, left=self.NIL_LEAF, right=self.NIL_LEAF) 73 | self.count += 1 74 | return 75 | parent, node_dir = self._find_parent(value) 76 | if node_dir is None: 77 | return # value is in the tree 78 | new_node = Node(value=value, color=RED, parent=parent, left=self.NIL_LEAF, right=self.NIL_LEAF) 79 | if node_dir == 'L': 80 | parent.left = new_node 81 | else: 82 | parent.right = new_node 83 | 84 | self._try_rebalance(new_node) 85 | self.count += 1 86 | 87 | def remove(self, value): 88 | """ 89 | Try to get a node with 0 or 1 children. 90 | Either the node we're given has 0 or 1 children or we get its successor. 91 | """ 92 | node_to_remove = self.find_node(value) 93 | if node_to_remove is None: # node is not in the tree 94 | return 95 | if node_to_remove.get_children_count() == 2: 96 | # find the in-order successor and replace its value. 97 | # then, remove the successor 98 | successor = self._find_in_order_successor(node_to_remove) 99 | node_to_remove.value = successor.value # switch the value 100 | node_to_remove = successor 101 | 102 | # has 0 or 1 children! 103 | self._remove(node_to_remove) 104 | self.count -= 1 105 | 106 | def contains(self, value) -> bool: 107 | """ Returns a boolean indicating if the given value is present in the tree """ 108 | return bool(self.find_node(value)) 109 | 110 | def ceil(self, value) -> int or None: 111 | """ 112 | Given a value, return the closest value that is equal or bigger than it, 113 | returning None when no such exists 114 | """ 115 | if self.root is None: return None 116 | last_found_val = None if self.root.value < value else self.root.value 117 | 118 | def find_ceil(node): 119 | nonlocal last_found_val 120 | if node == self.NIL_LEAF: 121 | return None 122 | if node.value == value: 123 | last_found_val = node.value 124 | return node.value 125 | elif node.value < value: 126 | # go right 127 | return find_ceil(node.right) 128 | else: 129 | # this node is bigger, save its value and go left 130 | last_found_val = node.value 131 | 132 | return find_ceil(node.left) 133 | find_ceil(self.root) 134 | return last_found_val 135 | 136 | def floor(self, value) -> int or None: 137 | """ 138 | Given a value, return the closest value that is equal or less than it, 139 | returning None when no such exists 140 | """ 141 | if self.root is None: return None 142 | last_found_val = None if self.root.value > value else self.root.value 143 | 144 | def find_floor(node): 145 | nonlocal last_found_val 146 | if node == self.NIL_LEAF: 147 | return None 148 | if node.value == value: 149 | last_found_val = node.value 150 | return node.value 151 | elif node.value < value: 152 | # this node is smaller, save its value and go right, trying to find a cloer one 153 | last_found_val = node.value 154 | 155 | return find_floor(node.right) 156 | else: 157 | return find_floor(node.left) 158 | 159 | find_floor(self.root) 160 | return last_found_val 161 | 162 | def _remove(self, node): 163 | """ 164 | Receives a node with 0 or 1 children (typically some sort of successor) 165 | and removes it according to its color/children 166 | :param node: Node with 0 or 1 children 167 | """ 168 | left_child = node.left 169 | right_child = node.right 170 | not_nil_child = left_child if left_child != self.NIL_LEAF else right_child 171 | if node == self.root: 172 | if not_nil_child != self.NIL_LEAF: 173 | # if we're removing the root and it has one valid child, simply make that child the root 174 | self.root = not_nil_child 175 | self.root.parent = None 176 | self.root.color = BLACK 177 | else: 178 | self.root = None 179 | elif node.color == RED: 180 | if not node.has_children(): 181 | # Red node with no children, the simplest remove 182 | self._remove_leaf(node) 183 | else: 184 | """ 185 | Since the node is red he cannot have a child. 186 | If he had a child, it'd need to be black, but that would mean that 187 | the black height would be bigger on the one side and that would make our tree invalid 188 | """ 189 | raise Exception('Unexpected behavior') 190 | else: # node is black! 191 | if right_child.has_children() or left_child.has_children(): # sanity check 192 | raise Exception('The red child of a black node with 0 or 1 children' 193 | ' cannot have children, otherwise the black height of the tree becomes invalid! ') 194 | if not_nil_child.color == RED: 195 | """ 196 | Swap the values with the red child and remove it (basically un-link it) 197 | Since we're a node with one child only, we can be sure that there are no nodes below the red child. 198 | """ 199 | node.value = not_nil_child.value 200 | node.left = not_nil_child.left 201 | node.right = not_nil_child.right 202 | else: # BLACK child 203 | # 6 cases :o 204 | self._remove_black_node(node) 205 | 206 | def _remove_leaf(self, leaf): 207 | """ Simply removes a leaf node by making it's parent point to a NIL LEAF""" 208 | if leaf.value >= leaf.parent.value: 209 | # in those weird cases where they're equal due to the successor swap 210 | leaf.parent.right = self.NIL_LEAF 211 | else: 212 | leaf.parent.left = self.NIL_LEAF 213 | 214 | def _remove_black_node(self, node): 215 | """ 216 | Loop through each case recursively until we reach a terminating case. 217 | What we're left with is a leaf node which is ready to be deleted without consequences 218 | """ 219 | self.__case_1(node) 220 | self._remove_leaf(node) 221 | 222 | def __case_1(self, node): 223 | """ 224 | Case 1 is when there's a double black node on the root 225 | Because we're at the root, we can simply remove it 226 | and reduce the black height of the whole tree. 227 | 228 | __|10B|__ __10B__ 229 | / \ ==> / \ 230 | 9B 20B 9B 20B 231 | """ 232 | if self.root == node: 233 | node.color = BLACK 234 | return 235 | self.__case_2(node) 236 | 237 | def __case_2(self, node): 238 | """ 239 | Case 2 applies when 240 | the parent is BLACK 241 | the sibling is RED 242 | the sibling's children are BLACK or NIL 243 | It takes the sibling and rotates it 244 | 245 | 40B 60B 246 | / \ --CASE 2 ROTATE--> / \ 247 | |20B| 60R LEFT ROTATE 40R 80B 248 | DBL BLACK IS 20----^ / \ SIBLING 60R / \ 249 | 50B 80B |20B| 50B 250 | (if the sibling's direction was left of it's parent, we would RIGHT ROTATE it) 251 | Now the original node's parent is RED 252 | and we can apply case 4 or case 6 253 | """ 254 | parent = node.parent 255 | sibling, direction = self._get_sibling(node) 256 | if sibling.color == RED and parent.color == BLACK and sibling.left.color != RED and sibling.right.color != RED: 257 | self.ROTATIONS[direction](node=None, parent=sibling, grandfather=parent) 258 | parent.color = RED 259 | sibling.color = BLACK 260 | return self.__case_1(node) 261 | self.__case_3(node) 262 | 263 | def __case_3(self, node): 264 | """ 265 | Case 3 deletion is when: 266 | the parent is BLACK 267 | the sibling is BLACK 268 | the sibling's children are BLACK 269 | Then, we make the sibling red and 270 | pass the double black node upwards 271 | 272 | Parent is black 273 | ___50B___ Sibling is black ___50B___ 274 | / \ Sibling's children are black / \ 275 | 30B 80B CASE 3 30B |80B| Continue with other cases 276 | / \ / \ ==> / \ / \ 277 | 20B 35R 70B |90B|<---REMOVE 20B 35R 70R X 278 | / \ / \ 279 | 34B 37B 34B 37B 280 | """ 281 | parent = node.parent 282 | sibling, _ = self._get_sibling(node) 283 | if (sibling.color == BLACK and parent.color == BLACK 284 | and sibling.left.color != RED and sibling.right.color != RED): 285 | # color the sibling red and forward the double black node upwards 286 | # (call the cases again for the parent) 287 | sibling.color = RED 288 | return self.__case_1(parent) # start again 289 | 290 | self.__case_4(node) 291 | 292 | def __case_4(self, node): 293 | """ 294 | If the parent is red and the sibling is black with no red children, 295 | simply swap their colors 296 | DB-Double Black 297 | __10R__ __10B__ The black height of the left subtree has been incremented 298 | / \ / \ And the one below stays the same 299 | DB 15B ===> X 15R No consequences, we're done! 300 | / \ / \ 301 | 12B 17B 12B 17B 302 | """ 303 | parent = node.parent 304 | if parent.color == RED: 305 | sibling, direction = self._get_sibling(node) 306 | if sibling.color == BLACK and sibling.left.color != RED and sibling.right.color != RED: 307 | parent.color, sibling.color = sibling.color, parent.color # switch colors 308 | return # Terminating 309 | self.__case_5(node) 310 | 311 | def __case_5(self, node): 312 | """ 313 | Case 5 is a rotation that changes the circumstances so that we can do a case 6 314 | If the closer node is red and the outer BLACK or NIL, we do a left/right rotation, depending on the orientation 315 | This will showcase when the CLOSER NODE's direction is RIGHT 316 | 317 | ___50B___ __50B__ 318 | / \ / \ 319 | 30B |80B| <-- Double black 35B |80B| Case 6 is now 320 | / \ / \ Closer node is red (35R) / \ / applicable here, 321 | 20B 35R 70R X Outer is black (20B) 30R 37B 70R so we redirect the node 322 | / \ So we do a LEFT ROTATION / \ to it :) 323 | 34B 37B on 35R (closer node) 20B 34B 324 | """ 325 | sibling, direction = self._get_sibling(node) 326 | closer_node = sibling.right if direction == 'L' else sibling.left 327 | outer_node = sibling.left if direction == 'L' else sibling.right 328 | if closer_node.color == RED and outer_node.color != RED and sibling.color == BLACK: 329 | if direction == 'L': 330 | self._left_rotation(node=None, parent=closer_node, grandfather=sibling) 331 | else: 332 | self._right_rotation(node=None, parent=closer_node, grandfather=sibling) 333 | closer_node.color = BLACK 334 | sibling.color = RED 335 | 336 | self.__case_6(node) 337 | 338 | def __case_6(self, node): 339 | """ 340 | Case 6 requires 341 | SIBLING to be BLACK 342 | OUTER NODE to be RED 343 | Then, does a right/left rotation on the sibling 344 | This will showcase when the SIBLING's direction is LEFT 345 | 346 | Double Black 347 | __50B__ | __35B__ 348 | / \ | / \ 349 | SIBLING--> 35B |80B| <- 30R 50R 350 | / \ / / \ / \ 351 | 30R 37B 70R Outer node is RED 20B 34B 37B 80B 352 | / \ Closer node doesn't / 353 | 20B 34B matter 70R 354 | Parent doesn't 355 | matter 356 | So we do a right rotation on 35B! 357 | """ 358 | sibling, direction = self._get_sibling(node) 359 | outer_node = sibling.left if direction == 'L' else sibling.right 360 | 361 | def __case_6_rotation(direction): 362 | parent_color = sibling.parent.color 363 | self.ROTATIONS[direction](node=None, parent=sibling, grandfather=sibling.parent) 364 | # new parent is sibling 365 | sibling.color = parent_color 366 | sibling.right.color = BLACK 367 | sibling.left.color = BLACK 368 | 369 | if sibling.color == BLACK and outer_node.color == RED: 370 | return __case_6_rotation(direction) # terminating 371 | 372 | raise Exception('We should have ended here, something is wrong') 373 | 374 | def _try_rebalance(self, node): 375 | """ 376 | Given a red child node, determine if there is a need to rebalance (if the parent is red) 377 | If there is, rebalance it 378 | """ 379 | parent = node.parent 380 | value = node.value 381 | if (parent is None # what the fuck? (should not happen) 382 | or parent.parent is None # parent is the root 383 | or (node.color != RED or parent.color != RED)): # no need to rebalance 384 | return 385 | grandfather = parent.parent 386 | node_dir = 'L' if parent.value > value else 'R' 387 | parent_dir = 'L' if grandfather.value > parent.value else 'R' 388 | uncle = grandfather.right if parent_dir == 'L' else grandfather.left 389 | general_direction = node_dir + parent_dir 390 | 391 | if uncle == self.NIL_LEAF or uncle.color == BLACK: 392 | # rotate 393 | if general_direction == 'LL': 394 | self._right_rotation(node, parent, grandfather, to_recolor=True) 395 | elif general_direction == 'RR': 396 | self._left_rotation(node, parent, grandfather, to_recolor=True) 397 | elif general_direction == 'LR': 398 | self._right_rotation(node=None, parent=node, grandfather=parent) 399 | # due to the prev rotation, our node is now the parent 400 | self._left_rotation(node=parent, parent=node, grandfather=grandfather, to_recolor=True) 401 | elif general_direction == 'RL': 402 | self._left_rotation(node=None, parent=node, grandfather=parent) 403 | # due to the prev rotation, our node is now the parent 404 | self._right_rotation(node=parent, parent=node, grandfather=grandfather, to_recolor=True) 405 | else: 406 | raise Exception("{} is not a valid direction!".format(general_direction)) 407 | else: # uncle is RED 408 | self._recolor(grandfather) 409 | 410 | def __update_parent(self, node, parent_old_child, new_parent): 411 | """ 412 | Our node 'switches' places with the old child 413 | Assigns a new parent to the node. 414 | If the new_parent is None, this means that our node becomes the root of the tree 415 | """ 416 | node.parent = new_parent 417 | if new_parent: 418 | # Determine the old child's position in order to put node there 419 | if new_parent.value > parent_old_child.value: 420 | new_parent.left = node 421 | else: 422 | new_parent.right = node 423 | else: 424 | self.root = node 425 | 426 | def _right_rotation(self, node, parent, grandfather, to_recolor=False): 427 | grand_grandfather = grandfather.parent 428 | self.__update_parent(node=parent, parent_old_child=grandfather, new_parent=grand_grandfather) 429 | 430 | old_right = parent.right 431 | parent.right = grandfather 432 | grandfather.parent = parent 433 | 434 | grandfather.left = old_right # save the old right values 435 | old_right.parent = grandfather 436 | 437 | if to_recolor: 438 | parent.color = BLACK 439 | node.color = RED 440 | grandfather.color = RED 441 | 442 | def _left_rotation(self, node, parent, grandfather, to_recolor=False): 443 | grand_grandfather = grandfather.parent 444 | self.__update_parent(node=parent, parent_old_child=grandfather, new_parent=grand_grandfather) 445 | 446 | old_left = parent.left 447 | parent.left = grandfather 448 | grandfather.parent = parent 449 | 450 | grandfather.right = old_left # save the old left values 451 | old_left.parent = grandfather 452 | 453 | if to_recolor: 454 | parent.color = BLACK 455 | node.color = RED 456 | grandfather.color = RED 457 | 458 | def _recolor(self, grandfather): 459 | grandfather.right.color = BLACK 460 | grandfather.left.color = BLACK 461 | if grandfather != self.root: 462 | grandfather.color = RED 463 | self._try_rebalance(grandfather) 464 | 465 | def _find_parent(self, value): 466 | """ Finds a place for the value in our binary tree""" 467 | def inner_find(parent): 468 | """ 469 | Return the appropriate parent node for our new node as well as the side it should be on 470 | """ 471 | if value == parent.value: 472 | return None, None 473 | elif parent.value < value: 474 | if parent.right.color == NIL: # no more to go 475 | return parent, 'R' 476 | return inner_find(parent.right) 477 | elif value < parent.value: 478 | if parent.left.color == NIL: # no more to go 479 | return parent, 'L' 480 | return inner_find(parent.left) 481 | 482 | return inner_find(self.root) 483 | 484 | def find_node(self, value): 485 | def inner_find(root): 486 | if root is None or root == self.NIL_LEAF: 487 | return None 488 | if value > root.value: 489 | return inner_find(root.right) 490 | elif value < root.value: 491 | return inner_find(root.left) 492 | else: 493 | return root 494 | 495 | found_node = inner_find(self.root) 496 | return found_node 497 | 498 | def _find_in_order_successor(self, node): 499 | right_node = node.right 500 | left_node = right_node.left 501 | if left_node == self.NIL_LEAF: 502 | return right_node 503 | while left_node.left != self.NIL_LEAF: 504 | left_node = left_node.left 505 | return left_node 506 | 507 | def _get_sibling(self, node): 508 | """ 509 | Returns the sibling of the node, as well as the side it is on 510 | e.g 511 | 512 | 20 (A) 513 | / \ 514 | 15(B) 25(C) 515 | 516 | _get_sibling(25(C)) => 15(B), 'R' 517 | """ 518 | parent = node.parent 519 | if node.value >= parent.value: 520 | sibling = parent.left 521 | direction = 'L' 522 | else: 523 | sibling = parent.right 524 | direction = 'R' 525 | return sibling, direction -------------------------------------------------------------------------------- /code-samples/latex.tex: -------------------------------------------------------------------------------- 1 | \documentclass{sigchi} 2 | 3 | % Use this command to override the default ACM copyright statement 4 | % (e.g. for preprints). Consult the conference website for the 5 | % camera-ready copyright statement. 6 | 7 | %% EXAMPLE BEGIN -- HOW TO OVERRIDE THE DEFAULT COPYRIGHT STRIP -- (July 22, 2013 - Paul Baumann) 8 | % \toappear{Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights for components of this work owned by others than ACM must be honored. Abstracting with credit is permitted. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Request permissions from permissions@acm.org. \\ 9 | % {\emph{CHI'14}}, April 26--May 1, 2014, Toronto, Canada. \\ 10 | % Copyright \copyright~2014 ACM ISBN/14/04...\$15.00. \\ 11 | % DOI string from ACM form confirmation} 12 | %% EXAMPLE END -- HOW TO OVERRIDE THE DEFAULT COPYRIGHT STRIP -- (July 22, 2013 - Paul Baumann) 13 | 14 | % Arabic page numbers for submission. Remove this line to eliminate 15 | % page numbers for the camera ready copy 16 | % \pagenumbering{arabic} 17 | 18 | % Load basic packages 19 | \usepackage{balance} % to better equalize the last page 20 | \usepackage{graphics} % for EPS, load graphicx instead 21 | %\usepackage[T1]{fontenc} 22 | \usepackage{txfonts} 23 | \usepackage{times} % comment if you want LaTeX's default font 24 | \usepackage[pdftex]{hyperref} 25 | % \usepackage{url} % llt: nicely formatted URLs 26 | \usepackage{color} 27 | \usepackage{textcomp} 28 | \usepackage{booktabs} 29 | \usepackage{ccicons} 30 | \usepackage{todonotes} 31 | 32 | % llt: Define a global style for URLs, rather that the default one 33 | \makeatletter 34 | \def\url@leostyle{% 35 | \@ifundefined{selectfont}{\def\UrlFont{\sf}}{\def\UrlFont{\small\bf\ttfamily}}} 36 | \makeatother 37 | \urlstyle{leo} 38 | 39 | % To make various LaTeX processors do the right thing with page size. 40 | \def\pprw{8.5in} 41 | \def\pprh{11in} 42 | \special{papersize=\pprw,\pprh} 43 | \setlength{\paperwidth}{\pprw} 44 | \setlength{\paperheight}{\pprh} 45 | \setlength{\pdfpagewidth}{\pprw} 46 | \setlength{\pdfpageheight}{\pprh} 47 | 48 | % Make sure hyperref comes last of your loaded packages, to give it a 49 | % fighting chance of not being over-written, since its job is to 50 | % redefine many LaTeX commands. 51 | \definecolor{linkColor}{RGB}{6,125,233} 52 | \hypersetup{% 53 | pdftitle={SIGCHI Conference Proceedings Format}, 54 | pdfauthor={LaTeX}, 55 | pdfkeywords={SIGCHI, proceedings, archival format}, 56 | bookmarksnumbered, 57 | pdfstartview={FitH}, 58 | colorlinks, 59 | citecolor=black, 60 | filecolor=black, 61 | linkcolor=black, 62 | urlcolor=linkColor, 63 | breaklinks=true, 64 | } 65 | 66 | % create a shortcut to typeset table headings 67 | % \newcommand\tabhead[1]{\small\textbf{#1}} 68 | 69 | % End of preamble. Here it comes the document. 70 | \begin{document} 71 | 72 | \title{SIGCHI Conference Proceedings Format} 73 | 74 | \numberofauthors{3} 75 | \author{% 76 | \alignauthor{1st Author Name\\ 77 | \affaddr{Affiliation}\\ 78 | \affaddr{City, Country}\\ 79 | \email{e-mail address}}\\ 80 | \alignauthor{2nd Author Name\\ 81 | \affaddr{Affiliation}\\ 82 | \affaddr{City, Country}\\ 83 | \email{e-mail address}}\\ 84 | \alignauthor{3rd Author Name\\ 85 | \affaddr{Affiliation}\\ 86 | \affaddr{City, Country}\\ 87 | \email{e-mail address}}\\ 88 | } 89 | 90 | \maketitle 91 | 92 | \begin{abstract} 93 | UPDATED---\today. This sample paper describes the formatting 94 | requirements for SIGCHI conference proceedings, and offers 95 | recommendations on writing for the worldwide SIGCHI 96 | readership. Please review this document even if you have submitted 97 | to SIGCHI conferences before, as some format details have changed 98 | relative to previous years. Abstracts should be about 150 words and 99 | are required. 100 | \end{abstract} 101 | 102 | \category{H.5.m.}{Information Interfaces and Presentation 103 | (e.g. HCI)}{Miscellaneous} \category{See 104 | \url{http://acm.org/about/class/1998/} for the full list of ACM 105 | classifiers. This section is required.}{}{} 106 | 107 | \keywords{Authors' choice; of terms; separated; by semi\-colons; 108 | commas, within terms only; this section is required.} 109 | 110 | \section{Introduction} 111 | 112 | This format is to be used for submissions that are published in the 113 | conference proceedings. We wish to give this volume a consistent, 114 | high-quality appearance. We therefore ask that authors follow some 115 | simple guidelines. You should format your paper exactly like this 116 | document. The easiest way to do this is to replace the content with 117 | your own material. This document describes how to prepare your 118 | submissions using \LaTeX. 119 | 120 | \section{Page Size and Columns} 121 | On each page your material should fit within a rectangle of 7 $\times$ 122 | 9.25 inches (18 $\times$ 23.5 cm), centered on a US Letter page (8.5 123 | $\times$ 11 inches), beginning 0.75 inches (1.9 cm) from the top of 124 | the page, with a 0.33 inches (0.85 cm) space between two 3.3 inches 125 | (8.4 cm) columns. Right margins should be justified, not 126 | ragged. Please be sure your document and PDF are US letter and not A4. 127 | 128 | \section{Typeset Text} 129 | The styles contained in this document have been modified from the 130 | default styles to reflect ACM formatting conventions. For example, 131 | content paragraphs like this one are formatted using the Normal style. 132 | 133 | \LaTeX\ sometimes will create overfull lines that extend into columns. 134 | To attempt to combat this, the \texttt{.cls} file has a command, 135 | \texttt{{\textbackslash}sloppy}, that essentially asks \LaTeX\ to 136 | prefer underfull lines with extra whitespace. For more details on 137 | this, and info on how to control it more finely, check out 138 | {\url{http://www.economics.utoronto.ca/osborne/latex/PMAKEUP.HTM}}. 139 | 140 | \subsection{Title and Authors} 141 | 142 | Your paper's title, authors and affiliations should run across the 143 | full width of the page in a single column 17.8 cm (7 in.) wide. The 144 | title should be in Helvetica or Arial 18-point bold. Authors' names 145 | should be in Times New Roman or Times Roman 12-point bold, and 146 | affiliations in 12-point regular. 147 | 148 | See \texttt{{\textbackslash}author} section of this template for 149 | instructions on how to format the authors. For more than three 150 | authors, you may have to place some address information in a footnote, 151 | or in a named section at the end of your paper. Leave one 10-point 152 | line of white space below the last line of affiliations. 153 | 154 | \subsection{Abstract and Keywords} 155 | 156 | Every submission should begin with an abstract of about 150 words, 157 | followed by a set of Author Keywords and ACM Classification 158 | Keywords. The abstract and keywords should be placed in the left 159 | column of the first page under the left half of the title. The 160 | abstract should be a concise statement of the problem, approach, and 161 | conclusions of the work described. It should clearly state the paper's 162 | contribution to the field of HCI\@. 163 | 164 | \subsection{Normal or Body Text} 165 | 166 | Please use a 10-point Times New Roman or Times Roman font or, if this 167 | is unavailable, another proportional font with serifs, as close as 168 | possible in appearance to Times Roman 10-point. Other than Helvetica 169 | or Arial headings, please use sans-serif or non-proportional fonts 170 | only for special purposes, such as source code text. 171 | 172 | \subsection{First Page Copyright Notice} 173 | This template include a sample ACM copyright notice at the bottom of 174 | page 1, column 1. Upon acceptance, you will be provided with the 175 | appropriate copyright statement and unique DOI string for publication. 176 | Accepted papers will be distributed in the conference 177 | publications. They will also be placed in the ACM Digital Library, 178 | where they will remain accessible to thousands of researchers and 179 | practitioners worldwide. See 180 | \url{http://acm.org/publications/policies/copyright_policy} for the 181 | ACM’s copyright and permissions policy. 182 | 183 | \subsection{Subsequent Pages} 184 | 185 | On pages beyond the first, start at the top of the page and continue 186 | in double-column format. The two columns on the last page should be 187 | of equal length. 188 | 189 | \begin{figure} 190 | \centering 191 | \includegraphics[width=0.9\columnwidth]{figures/sigchi-logo} 192 | \caption{Insert a caption below each figure. Do not alter the 193 | Caption style.}~\label{fig:figure1} 194 | \end{figure} 195 | 196 | \subsection{References and Citations} 197 | 198 | Use a numbered list of references at the end of the article, ordered 199 | alphabetically by last name of first author, and referenced by numbers 200 | in 201 | brackets~\cite{acm_categories,ethics,Klemmer:2002:WSC:503376.503378}. 202 | Your references should be published materials accessible to the 203 | public. Internal technical reports may be cited only if they are 204 | easily accessible (i.e., you provide the address for obtaining the 205 | report within your citation) and may be obtained by any reader for a 206 | nominal fee. Proprietary information may not be cited. Private 207 | communications should be acknowledged in the main text, not referenced 208 | (e.g., ``[Borriello, personal communication]''). 209 | 210 | References should be in ACM citation format: 211 | \url{http://acm.org/publications/submissions/latex_style}. This 212 | includes citations to internet 213 | resources~\cite{acm_categories,cavender:writing,CHINOSAUR:venue,psy:gangnam} 214 | according to ACM format, although it is often appropriate to include 215 | URLs directly in the text, as above. 216 | 217 | 218 | % Use a numbered list of references at the end of the article, ordered 219 | % alphabetically by first author, and referenced by numbers in 220 | % brackets~\cite{ethics, Klemmer:2002:WSC:503376.503378, 221 | % Mather:2000:MUT, Zellweger:2001:FAO:504216.504224}. For papers from 222 | % conference proceedings, include the title of the paper and an 223 | % abbreviated name of the conference (e.g., for Interact 2003 224 | % proceedings, use \textit{Proc. Interact 2003}). Do not include the 225 | % location of the conference or the exact date; do include the page 226 | % numbers if available. See the examples of citations at the end of this 227 | % document. Within this template file, use the \texttt{References} style 228 | % for the text of your citation. 229 | 230 | % Your references should be published materials accessible to the 231 | % public. Internal technical reports may be cited only if they are 232 | % easily accessible (i.e., you provide the address for obtaining the 233 | % report within your citation) and may be obtained by any reader for a 234 | % nominal fee. Proprietary information may not be cited. Private 235 | % communications should be acknowledged in the main text, not referenced 236 | % (e.g., ``[Robertson, personal communication]''). 237 | 238 | \begin{table} 239 | \centering 240 | \begin{tabular}{r c c} 241 | \toprule 242 | & \multicolumn{2}{c}{\small{\textbf{Caption}}} \\ 243 | \cmidrule(r){2-3} 244 | {\small\textbf{Objects}} 245 | & {\small \textit{Pre-2002}} 246 | & {\small \textit{Current}} \\ 247 | \midrule 248 | Tables & Above & Below \\ 249 | Figures & Below & Below \\ 250 | \bottomrule 251 | \end{tabular} 252 | \caption{Table captions should be placed below the table. We 253 | recommend table lines be 1 point, 25\% black. Minimize use of 254 | unnecessary table lines.}~\label{tab:table1} 255 | \end{table} 256 | 257 | \section{Sections} 258 | 259 | The heading of a section should be in Helvetica or Arial 9-point bold, 260 | all in capitals. Sections should \textit{not} be numbered. 261 | 262 | \subsection{Subsections} 263 | 264 | Headings of subsections should be in Helvetica or Arial 9-point bold 265 | with initial letters capitalized. For sub-sections and 266 | sub-subsections, a word like \emph{the} or \emph{of} is not 267 | capitalized unless it is the first word of the heading. 268 | 269 | \subsubsection{Sub-subsections} 270 | 271 | Headings for sub-subsections should be in Helvetica or Arial 9-point 272 | italic with initial letters capitalized. Standard 273 | \texttt{{\textbackslash}section}, \texttt{{\textbackslash}subsection}, 274 | and \texttt{{\textbackslash}subsubsection} commands will work fine in 275 | this template. 276 | 277 | \section{Figures/Captions} 278 | 279 | Place figures and tables at the top or bottom of the appropriate 280 | column or columns, on the same page as the relevant text (see 281 | Figure~\ref{fig:figure1}). A figure or table may extend across both 282 | columns to a maximum width of 17.78 cm (7 in.). 283 | 284 | \begin{figure*} 285 | \centering 286 | \includegraphics[width=2\columnwidth]{figures/map} 287 | \caption{In this image, the map maximizes use of space. You can make 288 | figures as wide as you need, up to a maximum of the full width of 289 | both columns. Note that \LaTeX\ tends to render large figures on a 290 | dedicated page. Image: \ccbynd~ayman on 291 | Flickr.}~\label{fig:figure2} 292 | \end{figure*} 293 | 294 | Captions should be Times New Roman or Times Roman 9-point bold. They 295 | should be numbered (e.g., ``Table~\ref{tab:table1}'' or 296 | ``Figure~\ref{fig:figure1}''), centered and placed beneath the figure 297 | or table. Please note that the words ``Figure'' and ``Table'' should 298 | be spelled out (e.g., ``Figure'' rather than ``Fig.'') wherever they 299 | occur. Figures, like Figure~\ref{fig:figure2}, may span columns and 300 | all figures should also include alt text for improved accessibility. 301 | Papers and notes may use color figures, which are included in the page 302 | limit; the figures must be usable when printed in black-and-white in 303 | the proceedings. 304 | 305 | The paper may be accompanied by a short video figure up to five 306 | minutes in length. However, the paper should stand on its own without 307 | the video figure, as the video may not be available to everyone who 308 | reads the paper. 309 | 310 | \subsection{Inserting Images} 311 | When possible, include a vector formatted graphic (i.e. PDF or EPS). 312 | When including bitmaps, use an image editing tool to resize the image 313 | at the appropriate printing resolution (usually 300 dpi). 314 | 315 | \section{Language, Style and Content} 316 | 317 | The written and spoken language of SIGCHI is English. Spelling and 318 | punctuation may use any dialect of English (e.g., British, Canadian, 319 | US, etc.) provided this is done consis- tently. Hyphenation is 320 | optional. To ensure suitability for an international audience, please 321 | pay attention to the following: 322 | 323 | \begin{itemize} 324 | \item Write in a straightforward style. 325 | \item Try to avoid long or complex sentence structures. 326 | \item Briefly define or explain all technical terms that may be 327 | unfamiliar to readers. 328 | \item Explain all acronyms the first time they are used in your 329 | text---e.g., ``Digital Signal Processing (DSP)''. 330 | \item Explain local references (e.g., not everyone knows all city 331 | names in a particular country). 332 | \item Explain ``insider'' comments. Ensure that your whole audience 333 | understands any reference whose meaning you do not describe (e.g., 334 | do not assume that everyone has used a Macintosh or a particular 335 | application). 336 | \item Explain colloquial language and puns. Understanding phrases like 337 | ``red herring'' may require a local knowledge of English. Humor and 338 | irony are difficult to translate. 339 | \item Use unambiguous forms for culturally localized concepts, such as 340 | times, dates, currencies, and numbers (e.g., ``1--5--97'' or 341 | ``5/1/97'' may mean 5 January or 1 May, and ``seven o'clock'' may 342 | mean 7:00 am or 19:00). For currencies, indicate equivalences: 343 | ``Participants were paid {\fontfamily{txr}\selectfont \textwon} 344 | 25,000, or roughly US \$22.'' 345 | \item Be careful with the use of gender-specific pronouns (he, she) 346 | and other gendered words (chairman, manpower, man-months). Use 347 | inclusive language that is gender-neutral (e.g., she or he, they, 348 | s/he, chair, staff, staff-hours, person-years). See the 349 | \textit{Guidelines for Bias-Free Writing} for further advice and 350 | examples regarding gender and other personal 351 | attributes~\cite{Schwartz:1995:GBF}. Be particularly aware of 352 | considerations around writing about people with disabilities. 353 | \item If possible, use the full (extended) alphabetic character set 354 | for names of persons, institutions, and places (e.g., 355 | Gr{\o}nb{\ae}k, Lafreni\'ere, S\'anchez, Nguy\~{\^{e}}n, 356 | Universit{\"a}t, Wei{\ss}enbach, Z{\"u}llighoven, \r{A}rhus, etc.). 357 | These characters are already included in most versions and variants 358 | of Times, Helvetica, and Arial fonts. 359 | \end{itemize} 360 | 361 | \section{Accessibility} 362 | The Executive Council of SIGCHI has committed to making SIGCHI 363 | conferences more inclusive for researchers, practitioners, and 364 | educators with disabilities. As a part of this goal, the all authors 365 | are asked to work on improving the accessibility of their 366 | submissions. Specifically, we encourage authors to carry out the 367 | following five steps: 368 | \begin{enumerate} 369 | \item Add alternative text to all figures 370 | \item Mark table headings 371 | \item Add tags to the PDF 372 | \item Verify the default language 373 | \item Set the tab order to ``Use Document Structure'' 374 | \end{enumerate} 375 | For more information and links to instructions and resources, please 376 | see: \url{http://chi2016.acm.org/accessibility}. The 377 | \texttt{{\textbackslash}hyperref} package allows you to create well tagged PDF files, 378 | please see the preamble of this template for an example. 379 | 380 | \section{Page Numbering, Headers and Footers} 381 | Your final submission should not contain footer or header information 382 | at the top or bottom of each page. Specifically, your final submission 383 | should not include page numbers. Initial submissions may include page 384 | numbers, but these must be removed for camera-ready. Page numbers will 385 | be added to the PDF when the proceedings are assembled. 386 | 387 | \section{Producing and Testing PDF Files} 388 | 389 | We recommend that you produce a PDF version of your submission well 390 | before the final deadline. Your PDF file must be ACM DL 391 | Compliant. The requirements for an ACM Compliant PDF are available at: 392 | {\url{http://www.sheridanprinting.com/typedept/ACM-distilling-settings.htm}}. 393 | 394 | Test your PDF file by viewing or printing it with the same software we 395 | will use when we receive it, Adobe Acrobat Reader Version 10. This is 396 | widely available at no cost. Note that most 397 | reviewers will use a North American/European version of Acrobat 398 | reader, so please check your PDF accordingly. 399 | 400 | When creating your PDF from Word, ensure that you generate a tagged 401 | PDF from improved accessibility. This can be done by using the Adobe 402 | PDF add-in, also called PDFMaker. Select Acrobat | Preferences from 403 | the ribbon and ensure that ``Enable Accessibility and Reflow with 404 | tagged Adobe PDF'' is selected. You can then generate a tagged PDF by 405 | selecting ``Create PDF'' from the Acrobat ribbon. 406 | 407 | \section{Conclusion} 408 | 409 | It is important that you write for the SIGCHI audience. Please read 410 | previous years’ proceedings to understand the writing style and 411 | conventions that successful authors have used. It is particularly 412 | important that you state clearly what you have done, not merely what 413 | you plan to do, and explain how your work is different from previously 414 | published work, i.e., the unique contribution that your work makes to 415 | the field. Please consider what the reader will learn from your 416 | submission, and how they will find your work useful. If you write with 417 | these questions in mind, your work is more likely to be successful, 418 | both in being accepted into the conference, and in influencing the 419 | work of our field. 420 | 421 | \section{Acknowledgments} 422 | 423 | Sample text: We thank all the volunteers, and all publications support 424 | and staff, who wrote and provided helpful comments on previous 425 | versions of this document. Authors 1, 2, and 3 gratefully acknowledge 426 | the grant from NSF (\#1234--2012--ABC). \textit{This whole paragraph is 427 | just an example.} 428 | 429 | % Balancing columns in a ref list is a bit of a pain because you 430 | % either use a hack like flushend or balance, or manually insert 431 | % a column break. http://www.tex.ac.uk/cgi-bin/texfaq2html?label=balance 432 | % multicols doesn't work because we're already in two-column mode, 433 | % and flushend isn't awesome, so I choose balance. See this 434 | % for more info: http://cs.brown.edu/system/software/latex/doc/balance.pdf 435 | % 436 | % Note that in a perfect world balance wants to be in the first 437 | % column of the last page. 438 | % 439 | % If balance doesn't work for you, you can remove that and 440 | % hard-code a column break into the bbl file right before you 441 | % submit: 442 | % 443 | % http://stackoverflow.com/questions/2149854/how-to-manually-equalize-columns- 444 | % in-an-ieee-paper-if-using-bibtex 445 | % 446 | % Or, just remove \balance and give up on balancing the last page. 447 | % 448 | \balance{} 449 | 450 | \section{References Format} 451 | Your references should be published materials accessible to the 452 | public. Internal technical reports may be cited only if they are 453 | easily accessible (i.e., you provide the address for obtaining the 454 | report within your citation) and may be obtained by any reader for a 455 | nominal fee. Proprietary information may not be cited. Private 456 | communications should be acknowledged in the main text, not referenced 457 | (e.g., ``[Golovchinsky, personal communication]''). 458 | 459 | Use a numbered list of references at the end of the article, ordered 460 | alphabetically by first author, and referenced by numbers in 461 | brackets~\cite{ethics,Klemmer:2002:WSC:503376.503378}. For papers from 462 | conference proceedings, include the title of the paper and an 463 | abbreviated name of the conference (e.g., for Interact 2003 464 | proceedings, use Proc.\ Interact 2003). Do not include the location of 465 | the conference or the exact date; do include the page numbers if 466 | available. See the examples of citations at the end of this document 467 | and in the accompanying \texttt{BibTeX} document. 468 | 469 | References \textit{must be the same font size as other body 470 | text}. References should be in alphabetical order by last name of 471 | first author. Example reference formatting for individual journal 472 | articles~\cite{ethics}, articles in conference 473 | proceedings~\cite{Klemmer:2002:WSC:503376.503378}, 474 | books~\cite{Schwartz:1995:GBF}, theses~\cite{sutherland:sketchpad}, 475 | book chapters~\cite{winner:politics}, a journal issue~\cite{kaye:puc}, 476 | websites~\cite{acm_categories,cavender:writing}, 477 | tweets~\cite{CHINOSAUR:venue}, patents~\cite{heilig:sensorama}, and 478 | online videos~\cite{psy:gangnam} is given here. This formatting is a 479 | slightly abbreviated version of the format automatically generated by 480 | the ACM Digital Library (\url{http://dl.acm.org}) as ``ACM Ref''. More 481 | details of reference formatting are available at: 482 | \url{http://www.acm.org/publications/submissions/latex_style}. 483 | 484 | % REFERENCES FORMAT 485 | % References must be the same font size as other body text. 486 | \bibliographystyle{SIGCHI-Reference-Format} 487 | \bibliography{sample} 488 | 489 | \end{document} 490 | 491 | %%% Local Variables: 492 | %%% mode: latex 493 | %%% TeX-master: t 494 | %%% End: 495 | -------------------------------------------------------------------------------- /code-samples/cpp.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | #include "addrman.h" 4 | #include "arith_uint256.h" 5 | #include "buip055fork.h" 6 | #include "chainparams.h" 7 | #include "checkpoints.h" 8 | #include "checkqueue.h" 9 | #include "clientversion.h" 10 | #include "connmgr.h" 11 | #include "consensus/consensus.h" 12 | #include "consensus/merkle.h" 13 | #include "consensus/validation.h" 14 | #include "dosman.h" 15 | #include "expedited.h" 16 | #include "hash.h" 17 | #include "init.h" 18 | #include "merkleblock.h" 19 | #include "net.h" 20 | #include "nodestate.h" 21 | #include "parallel.h" 22 | #include "policy/policy.h" 23 | #include "pow.h" 24 | #include "primitives/block.h" 25 | #include "primitives/transaction.h" 26 | #include "requestManager.h" 27 | #include "script/script.h" 28 | #include "script/sigcache.h" 29 | #include "script/standard.h" 30 | #include "thinblock.h" 31 | #include "tinyformat.h" 32 | #include "txdb.h" 33 | #include "txmempool.h" 34 | #include "ui_interface.h" 35 | #include "undo.h" 36 | #include "util.h" 37 | #include "utilmoneystr.h" 38 | #include "utilstrencodings.h" 39 | #include "validationinterface.h" 40 | #include "versionbits.h" 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #if defined(NDEBUG) 53 | #error "Bitcoin cannot be compiled without assertions." 54 | #endif 55 | 56 | /** 57 | * Global state 58 | */ 59 | 60 | // BU variables moved to globals.cpp 61 | // BU moved CCriticalSection cs_main; 62 | 63 | // BU moved BlockMap mapBlockIndex; 64 | // BU movedCChain chainActive; 65 | CBlockIndex *pindexBestHeader = NULL; 66 | 67 | CCoinsViewDB *pcoinsdbview = nullptr; 68 | 69 | int64_t nTimeBestReceived = 0; 70 | // BU moved CWaitableCriticalSection csBestBlock; 71 | // BU moved CConditionVariable cvBlockChange; 72 | bool fImporting = false; 73 | bool fReindex = false; 74 | bool fTxIndex = false; 75 | bool fHavePruned = false; 76 | bool fPruneMode = false; 77 | bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; 78 | bool fRequireStandard = true; 79 | unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; 80 | bool fCheckBlockIndex = false; 81 | bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; 82 | size_t nCoinCacheUsage = 5000 * 300; 83 | uint64_t nPruneTarget = 0; 84 | uint32_t nXthinBloomFilterSize = MAX_BLOOM_FILTER_SIZE; 85 | 86 | CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); 87 | 88 | // BU: Move global objects to a single file 89 | extern CTxMemPool mempool; 90 | 91 | 92 | // BU: start block download at low numbers in case our peers are slow when we start 93 | /** Number of blocks that can be requested at any given time from a single peer. */ 94 | static unsigned int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 1; 95 | /** Size of the "block download window": how far ahead of our current height do we fetch? 96 | * Larger windows tolerate larger download speed differences between peer, but increase the potential 97 | * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning 98 | * harder). We'll probably want to make this a per-peer adaptive value at some point. */ 99 | static unsigned int BLOCK_DOWNLOAD_WINDOW = 256; 100 | 101 | extern CTweak maxBlocksInTransitPerPeer; // override the above 102 | extern CTweak blockDownloadWindow; 103 | extern CTweak reindexTypicalBlockSize; 104 | 105 | extern std::map mapInboundConnectionTracker; 106 | extern CCriticalSection cs_mapInboundConnectionTracker; 107 | 108 | /** A cache to store headers that have arrived but can not yet be connected **/ 109 | std::map > mapUnConnectedHeaders; 110 | 111 | /** 112 | * Returns true if there are nRequired or more blocks of minVersion or above 113 | * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards. 114 | */ 115 | static bool IsSuperMajority(int minVersion, 116 | const CBlockIndex *pstart, 117 | unsigned nRequired, 118 | const Consensus::Params &consensusParams); 119 | static void CheckBlockIndex(const Consensus::Params &consensusParams); 120 | 121 | /** Constant stuff for coinbase transactions we create: */ 122 | CScript COINBASE_FLAGS; 123 | 124 | const std::string strMessageMagic = "Bitcoin Signed Message:\n"; 125 | 126 | extern CStatHistory nBlockValidationTime; 127 | extern CCriticalSection cs_blockvalidationtime; 128 | 129 | extern CCriticalSection cs_LastBlockFile; 130 | extern CCriticalSection cs_nBlockSequenceId; 131 | 132 | 133 | // Internal stuff 134 | namespace 135 | { 136 | struct CBlockIndexWorkComparator 137 | { 138 | bool operator()(CBlockIndex *pa, CBlockIndex *pb) const 139 | { 140 | // First sort by most total work, ... 141 | if (pa->nChainWork > pb->nChainWork) 142 | return false; 143 | if (pa->nChainWork < pb->nChainWork) 144 | return true; 145 | 146 | // ... then by earliest time received, ... 147 | if (pa->nSequenceId < pb->nSequenceId) 148 | return false; 149 | if (pa->nSequenceId > pb->nSequenceId) 150 | return true; 151 | 152 | // Use pointer address as tie breaker (should only happen with blocks 153 | // loaded from disk, as those all have id 0). 154 | if (pa < pb) 155 | return false; 156 | if (pa > pb) 157 | return true; 158 | 159 | // Identical blocks. 160 | return false; 161 | } 162 | }; 163 | 164 | CBlockIndex *pindexBestInvalid; 165 | 166 | /** 167 | * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and 168 | * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be 169 | * missing the data for the block. 170 | */ 171 | std::set setBlockIndexCandidates; 172 | /** Number of nodes with fSyncStarted. */ 173 | int nSyncStarted = 0; 174 | /** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions. 175 | * Pruned nodes may have entries where B is missing data. 176 | */ 177 | std::multimap mapBlocksUnlinked; 178 | 179 | std::vector vinfoBlockFile; 180 | int nLastBlockFile = 0; 181 | /** Global flag to indicate we should check to see if there are 182 | * block/undo files that should be deleted. Set on startup 183 | * or if we allocate more file space when we're in prune mode 184 | */ 185 | bool fCheckForPruning = false; 186 | 187 | /** 188 | * Every received block is assigned a unique and increasing identifier, so we 189 | * know which one to give priority in case of a fork. 190 | */ 191 | /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ 192 | uint32_t nBlockSequenceId = 1; 193 | 194 | /** 195 | * Sources of received blocks, saved to be able to send them reject 196 | * messages or ban them when processing happens afterwards. Protected by 197 | * cs_main. 198 | */ 199 | std::map mapBlockSource; 200 | 201 | 202 | uint256 hashRecentRejectsChainTip; 203 | 204 | /** Number of preferable block download peers. */ 205 | int nPreferredDownload = 0; 206 | 207 | /** Dirty block file entries. */ 208 | std::set setDirtyFileInfo; 209 | 210 | /** Number of peers from which we're downloading blocks. */ 211 | int nPeersWithValidatedDownloads = 0; 212 | } // anon namespace 213 | 214 | /** Dirty block index entries. */ 215 | std::set setDirtyBlockIndex; 216 | 217 | ////////////////////////////////////////////////////////////////////////////// 218 | // 219 | // Registration of network node signals. 220 | // 221 | 222 | namespace 223 | { 224 | int GetHeight() 225 | { 226 | LOCK(cs_main); 227 | return chainActive.Height(); 228 | } 229 | 230 | void UpdatePreferredDownload(CNode *node, CNodeState *state) 231 | { 232 | nPreferredDownload -= state->fPreferredDownload; 233 | 234 | // Whether this node should be marked as a preferred download node. 235 | state->fPreferredDownload = !node->fOneShot && !node->fClient; 236 | // BU allow downloads from inbound nodes; this may have been limited to stop attackers from connecting 237 | // and offering a bad chain. However, we are connecting to multiple nodes and so can choose the most work 238 | // chain on that basis. 239 | // state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; 240 | // LogPrint("net", "node %s preferred DL: %d because (%d || %d) && %d && %d\n", node->GetLogName(), 241 | // state->fPreferredDownload, !node->fInbound, node->fWhitelisted, !node->fOneShot, !node->fClient); 242 | 243 | nPreferredDownload += state->fPreferredDownload; 244 | } 245 | 246 | void InitializeNode(NodeId nodeid, const CNode *pnode) 247 | { 248 | LOCK(cs_main); 249 | CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; 250 | state.name = pnode->addrName; 251 | state.address = pnode->addr; 252 | } 253 | 254 | void FinalizeNode(NodeId nodeid) 255 | { 256 | LOCK(cs_main); 257 | CNodeState *state = State(nodeid); 258 | 259 | if (state->fSyncStarted) 260 | nSyncStarted--; 261 | 262 | BOOST_FOREACH (const QueuedBlock &entry, state->vBlocksInFlight) 263 | { 264 | mapBlocksInFlight.erase(entry.hash); 265 | } 266 | nPreferredDownload -= state->fPreferredDownload; 267 | nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); 268 | DbgAssert(nPeersWithValidatedDownloads >= 0, nPeersWithValidatedDownloads = 0); 269 | 270 | mapNodeState.erase(nodeid); 271 | 272 | if (mapNodeState.empty()) 273 | { 274 | // Do a consistency check after the last peer is removed. Force consistent state if production code 275 | DbgAssert(mapBlocksInFlight.empty(), mapBlocksInFlight.clear()); 276 | DbgAssert(nPreferredDownload == 0, nPreferredDownload = 0); 277 | DbgAssert(nPeersWithValidatedDownloads == 0, nPeersWithValidatedDownloads = 0); 278 | } 279 | } 280 | 281 | // Requires cs_main. 282 | // Returns a bool indicating whether we requested this block. 283 | bool MarkBlockAsReceived(const uint256 &hash) 284 | { 285 | std::map::iterator> >::iterator itInFlight = 286 | mapBlocksInFlight.find(hash); 287 | if (itInFlight != mapBlocksInFlight.end()) 288 | { 289 | // BUIP010 Xtreme Thinblocks: begin section 290 | int64_t getdataTime = itInFlight->second.second->nTime; 291 | int64_t now = GetTimeMicros(); 292 | double nResponseTime = (double)(now - getdataTime) / 1000000.0; 293 | 294 | // BU: calculate avg block response time over last 20 blocks to be used for IBD tuning 295 | // start at a higher number so that we don't start jamming IBD when we restart a node sync 296 | static double avgResponseTime = 5; 297 | static uint8_t blockRange = 20; 298 | if (avgResponseTime > 0) 299 | avgResponseTime -= (avgResponseTime / blockRange); 300 | avgResponseTime += nResponseTime / blockRange; 301 | if (avgResponseTime < 0.2) 302 | { 303 | MAX_BLOCKS_IN_TRANSIT_PER_PEER = 32; 304 | } 305 | else if (avgResponseTime < 0.5) 306 | { 307 | MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16; 308 | } 309 | else if (avgResponseTime < 0.9) 310 | { 311 | MAX_BLOCKS_IN_TRANSIT_PER_PEER = 8; 312 | } 313 | else if (avgResponseTime < 1.4) 314 | { 315 | MAX_BLOCKS_IN_TRANSIT_PER_PEER = 4; 316 | } 317 | else if (avgResponseTime < 2.0) 318 | { 319 | MAX_BLOCKS_IN_TRANSIT_PER_PEER = 2; 320 | } 321 | else 322 | { 323 | MAX_BLOCKS_IN_TRANSIT_PER_PEER = 1; 324 | } 325 | 326 | LogPrint("thin", "Received block %s in %.2f seconds\n", hash.ToString(), nResponseTime); 327 | LogPrint("thin", "Average block response time is %.2f seconds\n", avgResponseTime); 328 | if (maxBlocksInTransitPerPeer.value != 0) 329 | { 330 | MAX_BLOCKS_IN_TRANSIT_PER_PEER = maxBlocksInTransitPerPeer.value; 331 | } 332 | if (blockDownloadWindow.value != 0) 333 | { 334 | BLOCK_DOWNLOAD_WINDOW = blockDownloadWindow.value; 335 | } 336 | LogPrint("thin", "BLOCK_DOWNLOAD_WINDOW is %d MAX_BLOCKS_IN_TRANSIT_PER_PEER is %d\n", BLOCK_DOWNLOAD_WINDOW, 337 | MAX_BLOCKS_IN_TRANSIT_PER_PEER); 338 | 339 | { 340 | LOCK(cs_vNodes); 341 | BOOST_FOREACH (CNode *pnode, vNodes) 342 | { 343 | if (pnode->mapThinBlocksInFlight.size() > 0) 344 | { 345 | LOCK(pnode->cs_mapthinblocksinflight); 346 | if (pnode->mapThinBlocksInFlight.count(hash)) 347 | { 348 | // Only update thinstats if this is actually a thinblock and not a regular block. 349 | // Sometimes we request a thinblock but then revert to requesting a regular block 350 | // as can happen when the thinblock preferential timer is exceeded. 351 | thindata.UpdateResponseTime(nResponseTime); 352 | break; 353 | } 354 | } 355 | } 356 | } 357 | // BUIP010 Xtreme Thinblocks: end section 358 | CNodeState *state = State(itInFlight->second.first); 359 | state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; 360 | if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) 361 | { 362 | // Last validated block on the queue was received. 363 | nPeersWithValidatedDownloads--; 364 | } 365 | if (state->vBlocksInFlight.begin() == itInFlight->second.second) 366 | { 367 | // First block on the queue was received, update the start download time for the next one 368 | state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros()); 369 | } 370 | state->vBlocksInFlight.erase(itInFlight->second.second); 371 | state->nBlocksInFlight--; 372 | mapBlocksInFlight.erase(itInFlight); 373 | return true; 374 | } 375 | return false; 376 | } 377 | 378 | // BU MarkBlockAsInFlight moved out of anonymous namespace 379 | 380 | /** Check whether the last unknown block a peer advertised is not yet known. */ 381 | void ProcessBlockAvailability(NodeId nodeid) 382 | { 383 | CNodeState *state = State(nodeid); 384 | DbgAssert(state != NULL, return ); // node already destructed, nothing to do in production mode 385 | // AssertLockHeld(csMapBlockIndex); 386 | 387 | if (!state->hashLastUnknownBlock.IsNull()) 388 | { 389 | BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); 390 | if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) 391 | { 392 | if (state->pindexBestKnownBlock == NULL || 393 | itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) 394 | state->pindexBestKnownBlock = itOld->second; 395 | state->hashLastUnknownBlock.SetNull(); 396 | } 397 | } 398 | } 399 | 400 | 401 | // Requires cs_main 402 | bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex) 403 | { 404 | if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight)) 405 | return true; 406 | if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight)) 407 | return true; 408 | return false; 409 | } 410 | 411 | /** Find the last common ancestor two blocks have. 412 | * Both pa and pb must be non-NULL. */ 413 | CBlockIndex *LastCommonAncestor(CBlockIndex *pa, CBlockIndex *pb) 414 | { 415 | if (pa->nHeight > pb->nHeight) 416 | { 417 | pa = pa->GetAncestor(pb->nHeight); 418 | } 419 | else if (pb->nHeight > pa->nHeight) 420 | { 421 | pb = pb->GetAncestor(pa->nHeight); 422 | } 423 | 424 | while (pa != pb && pa && pb) 425 | { 426 | pa = pa->pprev; 427 | pb = pb->pprev; 428 | } 429 | 430 | // Eventually all chain branches meet at the genesis block. 431 | assert(pa == pb); 432 | return pa; 433 | } 434 | 435 | /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has 436 | * at most count entries. */ 437 | static unsigned int FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector &vBlocks) 438 | { 439 | unsigned int amtFound = 0; 440 | if (count == 0) 441 | return 0; 442 | 443 | vBlocks.reserve(vBlocks.size() + count); 444 | CNodeState *state = State(nodeid); 445 | DbgAssert(state != NULL, return 0); 446 | 447 | // Make sure pindexBestKnownBlock is up to date, we'll need it. 448 | ProcessBlockAvailability(nodeid); 449 | 450 | if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) 451 | { 452 | // This peer has nothing interesting. 453 | return 0; 454 | } 455 | 456 | if (state->pindexLastCommonBlock == NULL) 457 | { 458 | // Bootstrap quickly by guessing a parent of our best tip is the forking point. 459 | // Guessing wrong in either direction is not a problem. 460 | state->pindexLastCommonBlock = 461 | chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; 462 | } 463 | 464 | // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor 465 | // of its current tip anymore. Go back enough to fix that. 466 | state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); 467 | if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) 468 | return 0; 469 | 470 | std::vector vToFetch; 471 | CBlockIndex *pindexWalk = state->pindexLastCommonBlock; 472 | // Never fetch further than the current chain tip + the block download window. We need to ensure 473 | // the if running in pruning mode we don't download too many blocks ahead and as a result use to 474 | // much disk space to store unconnected blocks. 475 | int nWindowEnd = chainActive.Height() + BLOCK_DOWNLOAD_WINDOW; 476 | 477 | int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); 478 | while (pindexWalk->nHeight < nMaxHeight) 479 | { 480 | // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards 481 | // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive 482 | // as iterating over ~100 CBlockIndex* entries anyway. 483 | int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); 484 | vToFetch.resize(nToFetch); 485 | pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); 486 | vToFetch[nToFetch - 1] = pindexWalk; 487 | for (unsigned int i = nToFetch - 1; i > 0; i--) 488 | { 489 | vToFetch[i - 1] = vToFetch[i]->pprev; 490 | } 491 | 492 | // Iterate over those blocks in vToFetch (in forward direction), adding the ones that 493 | // are not yet downloaded and not in flight to vBlocks. In the mean time, update 494 | // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's 495 | // already part of our chain (and therefore don't need it even if pruned). 496 | BOOST_FOREACH (CBlockIndex *pindex, vToFetch) 497 | { 498 | if (!pindex->IsValid(BLOCK_VALID_TREE)) 499 | { 500 | // We consider the chain that this peer is on invalid. 501 | return amtFound; 502 | } 503 | if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) 504 | { 505 | if (pindex->nChainTx) 506 | state->pindexLastCommonBlock = pindex; 507 | } 508 | else 509 | { 510 | // Return if we've reached the end of the download window. 511 | if (pindex->nHeight > nWindowEnd) 512 | { 513 | return amtFound; 514 | } 515 | 516 | // Return if we've reached the end of the number of blocks we can download for this peer. 517 | vBlocks.push_back(pindex); 518 | amtFound += 1; 519 | if (vBlocks.size() == count) 520 | { 521 | return amtFound; 522 | } 523 | } 524 | } 525 | } 526 | return amtFound; 527 | } 528 | 529 | } // anon namespace 530 | 531 | /** Update tracking information about which blocks a peer is assumed to have. */ 532 | void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) 533 | { 534 | CNodeState *state = State(nodeid); 535 | DbgAssert(state != NULL, return ); // node already destructed, nothing to do in production mode 536 | 537 | ProcessBlockAvailability(nodeid); 538 | 539 | BlockMap::iterator it = mapBlockIndex.find(hash); 540 | if (it != mapBlockIndex.end() && it->second->nChainWork > 0) 541 | { 542 | // An actually better block was announced. 543 | if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) 544 | state->pindexBestKnownBlock = it->second; 545 | } 546 | else 547 | { 548 | // An unknown block was announced; just assume that the latest one is the best one. 549 | state->hashLastUnknownBlock = hash; 550 | } 551 | } 552 | 553 | void MarkBlockAsInFlight(NodeId nodeid, 554 | const uint256 &hash, 555 | const Consensus::Params &consensusParams, 556 | CBlockIndex *pindex = NULL) 557 | { 558 | LOCK(cs_main); 559 | CNodeState *state = State(nodeid); 560 | DbgAssert(state != NULL, return ); 561 | 562 | // If started then clear the thinblock timer used for preferential downloading 563 | thindata.ClearThinBlockTimer(hash); 564 | 565 | // BU why mark as received? because this erases it from the inflight list. Instead we'll check for it 566 | // BU removed: MarkBlockAsReceived(hash); 567 | std::map::iterator> >::iterator itInFlight = 568 | mapBlocksInFlight.find(hash); 569 | if (itInFlight == mapBlocksInFlight.end()) // If it hasn't already been marked inflight... 570 | { 571 | int64_t nNow = GetTimeMicros(); 572 | QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL}; 573 | std::list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); 574 | state->nBlocksInFlight++; 575 | state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; 576 | if (state->nBlocksInFlight == 1) 577 | { 578 | // We're starting a block download (batch) from this peer. 579 | state->nDownloadingSince = GetTimeMicros(); 580 | } 581 | if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) 582 | { 583 | nPeersWithValidatedDownloads++; 584 | } 585 | mapBlocksInFlight[hash] = std::make_pair(nodeid, it); 586 | } 587 | } 588 | 589 | // Requires cs_main 590 | bool CanDirectFetch(const Consensus::Params &consensusParams) 591 | { 592 | return !IsInitialBlockDownload(); 593 | // chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20; 594 | } 595 | 596 | bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) 597 | { 598 | CNodeRef node(connmgr->FindNodeFromId(nodeid)); 599 | if (!node) 600 | return false; 601 | 602 | LOCK(cs_main); 603 | CNodeState *state = State(nodeid); 604 | if (state == NULL) 605 | return false; 606 | stats.nMisbehavior = node->nMisbehavior; 607 | stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; 608 | stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; 609 | BOOST_FOREACH (const QueuedBlock &queue, state->vBlocksInFlight) 610 | { 611 | if (queue.pindex) 612 | stats.vHeightInFlight.push_back(queue.pindex->nHeight); 613 | } 614 | return true; 615 | } 616 | 617 | void RegisterNodeSignals(CNodeSignals &nodeSignals) 618 | { 619 | nodeSignals.GetHeight.connect(&GetHeight); 620 | nodeSignals.ProcessMessages.connect(&ProcessMessages); 621 | nodeSignals.SendMessages.connect(&SendMessages); 622 | nodeSignals.InitializeNode.connect(&InitializeNode); 623 | nodeSignals.FinalizeNode.connect(&FinalizeNode); 624 | } 625 | 626 | void UnregisterNodeSignals(CNodeSignals &nodeSignals) 627 | { 628 | nodeSignals.GetHeight.disconnect(&GetHeight); 629 | nodeSignals.ProcessMessages.disconnect(&ProcessMessages); 630 | nodeSignals.SendMessages.disconnect(&SendMessages); 631 | nodeSignals.InitializeNode.disconnect(&InitializeNode); 632 | nodeSignals.FinalizeNode.disconnect(&FinalizeNode); 633 | } 634 | 635 | CBlockIndex *FindForkInGlobalIndex(const CChain &chain, const CBlockLocator &locator) 636 | { 637 | // Find the first block the caller has in the main chain 638 | BOOST_FOREACH (const uint256 &hash, locator.vHave) 639 | { 640 | BlockMap::iterator mi = mapBlockIndex.find(hash); 641 | if (mi != mapBlockIndex.end()) 642 | { 643 | CBlockIndex *pindex = (*mi).second; 644 | if (chain.Contains(pindex)) 645 | return pindex; 646 | } 647 | } 648 | return chain.Genesis(); 649 | } 650 | 651 | CCoinsViewCache *pcoinsTip = NULL; 652 | CBlockTreeDB *pblocktree = NULL; 653 | 654 | bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) 655 | { 656 | if (tx.nLockTime == 0) 657 | return true; 658 | if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) 659 | return true; 660 | BOOST_FOREACH (const CTxIn &txin, tx.vin) 661 | { 662 | if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL)) 663 | return false; 664 | } 665 | return true; 666 | } 667 | 668 | bool CheckFinalTx(const CTransaction &tx, int flags) 669 | { 670 | // AssertLockHeld(cs_main); no longer needed, although caller may want to take to ensure chain does not advance 671 | 672 | // By convention a negative value for flags indicates that the 673 | // current network-enforced consensus rules should be used. In 674 | // a future soft-fork scenario that would mean checking which 675 | // rules would be enforced for the next block and setting the 676 | // appropriate flags. At the present time no soft-forks are 677 | // scheduled, so no flags are set. 678 | flags = std::max(flags, 0); 679 | 680 | CBlockIndex *tip = chainActive.Tip(); 681 | // CheckFinalTx() uses chainActive.Height()+1 to evaluate 682 | // nLockTime because when IsFinalTx() is called within 683 | // CBlock::AcceptBlock(), the height of the block *being* 684 | // evaluated is what is used. Thus if we want to know if a 685 | // transaction can be part of the *next* block, we need to call 686 | // IsFinalTx() with one more than chainActive.Height(). 687 | const int nBlockHeight = tip ? tip->nHeight + 1 : 0; 688 | 689 | // BIP113 will require that time-locked transactions have nLockTime set to 690 | // less than the median time of the previous block they're contained in. 691 | // When the next block is created its previous block will be the current 692 | // chain tip, so we use that to calculate the median time passed to 693 | // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set. 694 | const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? tip->GetMedianTimePast() : GetAdjustedTime(); 695 | 696 | return IsFinalTx(tx, nBlockHeight, nBlockTime); 697 | } 698 | 699 | /** 700 | * Calculates the block height and previous block's median time past at 701 | * which the transaction will be considered final in the context of BIP 68. 702 | * Also removes from the vector of input heights any entries which did not 703 | * correspond to sequence locked inputs as they do not affect the calculation. 704 | */ 705 | std::pair CalculateSequenceLocks(const CTransaction &tx, 706 | int flags, 707 | std::vector *prevHeights, 708 | const CBlockIndex &block) 709 | { 710 | assert(prevHeights->size() == tx.vin.size()); 711 | 712 | // Will be set to the equivalent height- and time-based nLockTime 713 | // values that would be necessary to satisfy all relative lock- 714 | // time constraints given our view of block chain history. 715 | // The semantics of nLockTime are the last invalid height/time, so 716 | // use -1 to have the effect of any height or time being valid. 717 | int nMinHeight = -1; 718 | int64_t nMinTime = -1; 719 | 720 | // tx.nVersion is signed integer so requires cast to unsigned otherwise 721 | // we would be doing a signed comparison and half the range of nVersion 722 | // wouldn't support BIP 68. 723 | bool fEnforceBIP68 = static_cast(tx.nVersion) >= 2 && flags & LOCKTIME_VERIFY_SEQUENCE; 724 | 725 | // Do not enforce sequence numbers as a relative lock time 726 | // unless we have been instructed to 727 | if (!fEnforceBIP68) 728 | { 729 | return std::make_pair(nMinHeight, nMinTime); 730 | } 731 | 732 | for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) 733 | { 734 | const CTxIn &txin = tx.vin[txinIndex]; 735 | 736 | // Sequence numbers with the most significant bit set are not 737 | // treated as relative lock-times, nor are they given any 738 | // consensus-enforced meaning at this point. 739 | if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) 740 | { 741 | // The height of this input is not relevant for sequence locks 742 | (*prevHeights)[txinIndex] = 0; 743 | continue; 744 | } 745 | 746 | int nCoinHeight = (*prevHeights)[txinIndex]; 747 | 748 | if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) 749 | { 750 | int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight - 1, 0))->GetMedianTimePast(); 751 | // NOTE: Subtract 1 to maintain nLockTime semantics 752 | // BIP 68 relative lock times have the semantics of calculating 753 | // the first block or time at which the transaction would be 754 | // valid. When calculating the effective block time or height 755 | // for the entire transaction, we switch to using the 756 | // semantics of nLockTime which is the last invalid block 757 | // time or height. Thus we subtract 1 from the calculated 758 | // time or height. 759 | 760 | // Time-based relative lock-times are measured from the 761 | // smallest allowed timestamp of the block containing the 762 | // txout being spent, which is the median time past of the 763 | // block prior. 764 | nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) 765 | << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 766 | 1); 767 | } 768 | else 769 | { 770 | nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1); 771 | } 772 | } 773 | 774 | return std::make_pair(nMinHeight, nMinTime); 775 | } -------------------------------------------------------------------------------- /code-samples/coffee.coffee: -------------------------------------------------------------------------------- 1 | # The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt 2 | # matches against the beginning of the source code. When a match is found, 3 | # a token is produced, we consume the match, and start again. Tokens are in the 4 | # form: 5 | # 6 | # [tag, value, locationData] 7 | # 8 | # where locationData is {first_line, first_column, last_line, last_column}, which is a 9 | # format that can be fed directly into [Jison](http://github.com/zaach/jison). These 10 | # are read by jison in the `parser.lexer` function defined in coffee-script.coffee. 11 | 12 | {Rewriter, INVERSES} = require './rewriter' 13 | 14 | # Import the helpers we need. 15 | {count, starts, compact, last, repeat, invertLiterate, 16 | locationDataToString, throwSyntaxError} = require './helpers' 17 | 18 | # The Lexer Class 19 | # --------------- 20 | 21 | # The Lexer class reads a stream of CoffeeScript and divvies it up into tagged 22 | # tokens. Some potential ambiguity in the grammar has been avoided by 23 | # pushing some extra smarts into the Lexer. 24 | exports.Lexer = class Lexer 25 | 26 | # **tokenize** is the Lexer's main method. Scan by attempting to match tokens 27 | # one at a time, using a regular expression anchored at the start of the 28 | # remaining code, or a custom recursive token-matching method 29 | # (for interpolations). When the next token has been recorded, we move forward 30 | # within the code past the token, and begin again. 31 | # 32 | # Each tokenizing method is responsible for returning the number of characters 33 | # it has consumed. 34 | # 35 | # Before returning the token stream, run it through the [Rewriter](rewriter.html). 36 | tokenize: (code, opts = {}) -> 37 | @literate = opts.literate # Are we lexing literate CoffeeScript? 38 | @indent = 0 # The current indentation level. 39 | @baseIndent = 0 # The overall minimum indentation level 40 | @indebt = 0 # The over-indentation at the current level. 41 | @outdebt = 0 # The under-outdentation at the current level. 42 | @indents = [] # The stack of all current indentation levels. 43 | @ends = [] # The stack for pairing up tokens. 44 | @tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`. 45 | 46 | @chunkLine = 47 | opts.line or 0 # The start line for the current @chunk. 48 | @chunkColumn = 49 | opts.column or 0 # The start column of the current @chunk. 50 | code = @clean code # The stripped, cleaned original source code. 51 | 52 | # At every position, run through this list of attempted matches, 53 | # short-circuiting if any of them succeed. Their order determines precedence: 54 | # `@literalToken` is the fallback catch-all. 55 | i = 0 56 | while @chunk = code[i..] 57 | consumed = 58 | @identifierToken() or 59 | @commentToken() or 60 | @whitespaceToken() or 61 | @lineToken() or 62 | @stringToken() or 63 | @numberToken() or 64 | @regexToken() or 65 | @jsToken() or 66 | @literalToken() 67 | 68 | # Update position 69 | [@chunkLine, @chunkColumn] = @getLineAndColumnFromChunk consumed 70 | 71 | i += consumed 72 | 73 | return {@tokens, index: i} if opts.untilBalanced and @ends.length is 0 74 | 75 | @closeIndentation() 76 | throwSyntaxError "missing #{end.tag}", end.origin[2] if end = @ends.pop() 77 | return @tokens if opts.rewrite is off 78 | (new Rewriter).rewrite @tokens 79 | 80 | # Preprocess the code to remove leading and trailing whitespace, carriage 81 | # returns, etc. If we're lexing literate CoffeeScript, strip external Markdown 82 | # by removing all lines that aren't indented by at least four spaces or a tab. 83 | clean: (code) -> 84 | code = code.slice(1) if code.charCodeAt(0) is BOM 85 | code = code.replace(/\r/g, '').replace TRAILING_SPACES, '' 86 | if WHITESPACE.test code 87 | code = "\n#{code}" 88 | @chunkLine-- 89 | code = invertLiterate code if @literate 90 | code 91 | 92 | # Tokenizers 93 | # ---------- 94 | 95 | # Matches identifying literals: variables, keywords, method names, etc. 96 | # Check to ensure that JavaScript reserved words aren't being used as 97 | # identifiers. Because CoffeeScript reserves a handful of keywords that are 98 | # allowed in JavaScript, we're careful not to tag them as keywords when 99 | # referenced as property names here, so you can still do `jQuery.is()` even 100 | # though `is` means `===` otherwise. 101 | identifierToken: -> 102 | return 0 unless match = IDENTIFIER.exec @chunk 103 | [input, id, colon] = match 104 | 105 | # Preserve length of id for location data 106 | idLength = id.length 107 | poppedToken = undefined 108 | 109 | if id is 'own' and @tag() is 'FOR' 110 | @token 'OWN', id 111 | return id.length 112 | if id is 'from' and @tag() is 'YIELD' 113 | @token 'FROM', id 114 | return id.length 115 | forcedIdentifier = colon or 116 | (prev = last @tokens) and (prev[0] in ['.', '?.', '::', '?::'] or 117 | not prev.spaced and prev[0] is '@') 118 | tag = 'IDENTIFIER' 119 | 120 | if not forcedIdentifier and (id in JS_KEYWORDS or id in COFFEE_KEYWORDS) 121 | tag = id.toUpperCase() 122 | if tag is 'WHEN' and @tag() in LINE_BREAK 123 | tag = 'LEADING_WHEN' 124 | else if tag is 'FOR' 125 | @seenFor = yes 126 | else if tag is 'UNLESS' 127 | tag = 'IF' 128 | else if tag in UNARY 129 | tag = 'UNARY' 130 | else if tag in RELATION 131 | if tag isnt 'INSTANCEOF' and @seenFor 132 | tag = 'FOR' + tag 133 | @seenFor = no 134 | else 135 | tag = 'RELATION' 136 | if @value() is '!' 137 | poppedToken = @tokens.pop() 138 | id = '!' + id 139 | 140 | if id in JS_FORBIDDEN 141 | if forcedIdentifier 142 | tag = 'IDENTIFIER' 143 | id = new String id 144 | id.reserved = yes 145 | else if id in RESERVED 146 | @error "reserved word \"#{id}\"" 147 | 148 | unless forcedIdentifier 149 | id = COFFEE_ALIAS_MAP[id] if id in COFFEE_ALIASES 150 | tag = switch id 151 | when '!' then 'UNARY' 152 | when '==', '!=' then 'COMPARE' 153 | when '&&', '||' then 'LOGIC' 154 | when 'true', 'false' then 'BOOL' 155 | when 'break', 'continue' then 'STATEMENT' 156 | else tag 157 | 158 | tagToken = @token tag, id, 0, idLength 159 | tagToken.variable = not forcedIdentifier 160 | if poppedToken 161 | [tagToken[2].first_line, tagToken[2].first_column] = 162 | [poppedToken[2].first_line, poppedToken[2].first_column] 163 | if colon 164 | colonOffset = input.lastIndexOf ':' 165 | @token ':', ':', colonOffset, colon.length 166 | 167 | input.length 168 | 169 | # Matches numbers, including decimals, hex, and exponential notation. 170 | # Be careful not to interfere with ranges-in-progress. 171 | numberToken: -> 172 | return 0 unless match = NUMBER.exec @chunk 173 | number = match[0] 174 | if /^0[BOX]/.test number 175 | @error "radix prefix '#{number}' must be lowercase" 176 | else if /E/.test(number) and not /^0x/.test number 177 | @error "exponential notation '#{number}' must be indicated with a lowercase 'e'" 178 | else if /^0\d*[89]/.test number 179 | @error "decimal literal '#{number}' must not be prefixed with '0'" 180 | else if /^0\d+/.test number 181 | @error "octal literal '#{number}' must be prefixed with '0o'" 182 | lexedLength = number.length 183 | if octalLiteral = /^0o([0-7]+)/.exec number 184 | number = '0x' + parseInt(octalLiteral[1], 8).toString 16 185 | if binaryLiteral = /^0b([01]+)/.exec number 186 | number = '0x' + parseInt(binaryLiteral[1], 2).toString 16 187 | @token 'NUMBER', number, 0, lexedLength 188 | lexedLength 189 | 190 | # Matches strings, including multi-line strings, as well as heredocs, with or without 191 | # interpolation. 192 | stringToken: -> 193 | [quote] = STRING_START.exec(@chunk) || [] 194 | return 0 unless quote 195 | regex = switch quote 196 | when "'" then STRING_SINGLE 197 | when '"' then STRING_DOUBLE 198 | when "'''" then HEREDOC_SINGLE 199 | when '"""' then HEREDOC_DOUBLE 200 | heredoc = quote.length is 3 201 | 202 | start = quote.length 203 | {tokens, index: end} = @matchWithInterpolations @chunk[start..], regex, quote, start 204 | $ = tokens.length - 1 205 | 206 | if heredoc 207 | # Find the smallest indentation. It will be removed from all lines later. 208 | indent = null 209 | doc = (token[1] for token, i in tokens when token[0] is 'NEOSTRING').join '#{}' 210 | while match = HEREDOC_INDENT.exec doc 211 | attempt = match[1] 212 | indent = attempt if indent is null or 0 < attempt.length < indent.length 213 | @mergeInterpolationTokens tokens, {quote: quote[0], start, end}, (value, i) => 214 | value = @formatString value 215 | value = value.replace LEADING_BLANK_LINE, '' if i is 0 216 | value = value.replace TRAILING_BLANK_LINE, '' if i is $ 217 | value = value.replace indentRegex, '' 218 | value = value.replace MULTILINER, '\\n' 219 | value 220 | else 221 | @mergeInterpolationTokens tokens, {quote, start, end}, (value, i) => 222 | value = @formatString value 223 | value = value.replace STRING_OMIT, (match, offset) -> 224 | if (i is 0 and offset is 0) or 225 | (i is $ and offset + match.length is value.length) 226 | '' 227 | else 228 | ' ' 229 | value 230 | 231 | end 232 | 233 | # Matches and consumes comments. 234 | commentToken: -> 235 | return 0 unless match = @chunk.match COMMENT 236 | [comment, here] = match 237 | if here 238 | if match = HERECOMMENT_ILLEGAL.exec comment 239 | @error "block comments cannot contain #{match[0]}", match.index 240 | comment.length 241 | 242 | # Matches JavaScript interpolated directly into the source via backticks. 243 | jsToken: -> 244 | return 0 unless @chunk.charAt(0) is '`' and match = JSTOKEN.exec @chunk 245 | @token 'JS', (script = match[0])[1...-1], 0, script.length 246 | script.length 247 | 248 | # Matches regular expression literals, as well as multiline extended ones. 249 | # Lexing regular expressions is difficult to distinguish from division, so we 250 | # borrow some basic heuristics from JavaScript and Ruby. 251 | regexToken: -> 252 | switch 253 | when match = REGEX_ILLEGAL.exec @chunk 254 | @error "regular expressions cannot begin with #{match[2]}", match.index + match[1].length 255 | when @chunk[...3] is '///' 256 | {tokens, index} = @matchWithInterpolations @chunk[3..], HEREGEX, '///', 3 257 | when match = REGEX.exec @chunk 258 | [regex, closed] = match 259 | index = regex.length 260 | prev = last @tokens 261 | if prev 262 | if prev.spaced and prev[0] in CALLABLE and not prev.stringEnd and not prev.regexEnd 263 | return 0 if not closed or POSSIBLY_DIVISION.test regex 264 | else if prev[0] in NOT_REGEX 265 | return 0 266 | @error 'missing / (unclosed regex)' unless closed 267 | else 268 | return 0 269 | 270 | [flags] = REGEX_FLAGS.exec @chunk[index..] 271 | end = index + flags.length 272 | switch 273 | when not VALID_FLAGS.test flags 274 | @error "invalid regular expression flags #{flags}", index 275 | when regex 276 | @token 'REGEX', "#{regex}#{flags}" 277 | when tokens.length is 1 278 | re = @formatHeregex(tokens[0][1]).replace(/\//g, '\\/') 279 | @token 'REGEX', "/#{ re or '(?:)' }/#{flags}" 280 | else 281 | @token 'IDENTIFIER', 'RegExp', 0, 0 282 | @token 'CALL_START', '(', 0, 0 283 | @mergeInterpolationTokens tokens, {quote: '"', start: 3, end}, (value) => 284 | @formatHeregex(value).replace(/\\/g, '\\\\') 285 | if flags 286 | @token ',', ',', index, 0 287 | @token 'STRING', '"' + flags + '"', index, flags.length 288 | rparen = @token ')', ')', end, 0 289 | rparen.regexEnd = true 290 | 291 | end 292 | 293 | # Matches newlines, indents, and outdents, and determines which is which. 294 | # If we can detect that the current line is continued onto the the next line, 295 | # then the newline is suppressed: 296 | # 297 | # elements 298 | # .each( ... ) 299 | # .map( ... ) 300 | # 301 | # Keeps track of the level of indentation, because a single outdent token 302 | # can close multiple indents, so we need to know how far in we happen to be. 303 | lineToken: -> 304 | return 0 unless match = MULTI_DENT.exec @chunk 305 | indent = match[0] 306 | @seenFor = no 307 | size = indent.length - 1 - indent.lastIndexOf '\n' 308 | noNewlines = @unfinished() 309 | if size - @indebt is @indent 310 | if noNewlines then @suppressNewlines() else @newlineToken 0 311 | return indent.length 312 | 313 | if size > @indent 314 | if noNewlines 315 | @indebt = size - @indent 316 | @suppressNewlines() 317 | return indent.length 318 | unless @tokens.length 319 | @baseIndent = @indent = size 320 | return indent.length 321 | diff = size - @indent + @outdebt 322 | @token 'INDENT', diff, indent.length - size, size 323 | @indents.push diff 324 | @ends.push {tag: 'OUTDENT'} 325 | @outdebt = @indebt = 0 326 | @indent = size 327 | else if size < @baseIndent 328 | @error 'missing indentation', indent.length 329 | else 330 | @indebt = 0 331 | @outdentToken @indent - size, noNewlines, indent.length 332 | indent.length 333 | 334 | # Record an outdent token or multiple tokens, if we happen to be moving back 335 | # inwards past several recorded indents. Sets new @indent value. 336 | outdentToken: (moveOut, noNewlines, outdentLength) -> 337 | decreasedIndent = @indent - moveOut 338 | while moveOut > 0 339 | lastIndent = @indents[@indents.length - 1] 340 | if not lastIndent 341 | moveOut = 0 342 | else if lastIndent is @outdebt 343 | moveOut -= @outdebt 344 | @outdebt = 0 345 | else if lastIndent < @outdebt 346 | @outdebt -= lastIndent 347 | moveOut -= lastIndent 348 | else 349 | dent = @indents.pop() + @outdebt 350 | if outdentLength and @chunk[outdentLength] in INDENTABLE_CLOSERS 351 | decreasedIndent -= dent - moveOut 352 | moveOut = dent 353 | @outdebt = 0 354 | # pair might call outdentToken, so preserve decreasedIndent 355 | @pair 'OUTDENT' 356 | @token 'OUTDENT', moveOut, 0, outdentLength 357 | moveOut -= dent 358 | @outdebt -= moveOut if dent 359 | @tokens.pop() while @value() is ';' 360 | 361 | @token 'TERMINATOR', '\n', outdentLength, 0 unless @tag() is 'TERMINATOR' or noNewlines 362 | @indent = decreasedIndent 363 | this 364 | 365 | # Matches and consumes non-meaningful whitespace. Tag the previous token 366 | # as being "spaced", because there are some cases where it makes a difference. 367 | whitespaceToken: -> 368 | return 0 unless (match = WHITESPACE.exec @chunk) or 369 | (nline = @chunk.charAt(0) is '\n') 370 | prev = last @tokens 371 | prev[if match then 'spaced' else 'newLine'] = true if prev 372 | if match then match[0].length else 0 373 | 374 | # Generate a newline token. Consecutive newlines get merged together. 375 | newlineToken: (offset) -> 376 | @tokens.pop() while @value() is ';' 377 | @token 'TERMINATOR', '\n', offset, 0 unless @tag() is 'TERMINATOR' 378 | this 379 | 380 | # Use a `\` at a line-ending to suppress the newline. 381 | # The slash is removed here once its job is done. 382 | suppressNewlines: -> 383 | @tokens.pop() if @value() is '\\' 384 | this 385 | 386 | # We treat all other single characters as a token. E.g.: `( ) , . !` 387 | # Multi-character operators are also literal tokens, so that Jison can assign 388 | # the proper order of operations. There are some symbols that we tag specially 389 | # here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish 390 | # parentheses that indicate a method call from regular parentheses, and so on. 391 | literalToken: -> 392 | if match = OPERATOR.exec @chunk 393 | [value] = match 394 | @tagParameters() if CODE.test value 395 | else 396 | value = @chunk.charAt 0 397 | tag = value 398 | prev = last @tokens 399 | if value is '=' and prev 400 | if not prev[1].reserved and prev[1] in JS_FORBIDDEN 401 | @error "reserved word \"#{@value()}\" can't be assigned" 402 | if prev[1] in ['||', '&&'] 403 | prev[0] = 'COMPOUND_ASSIGN' 404 | prev[1] += '=' 405 | return value.length 406 | if value is ';' 407 | @seenFor = no 408 | tag = 'TERMINATOR' 409 | else if value in MATH then tag = 'MATH' 410 | else if value in COMPARE then tag = 'COMPARE' 411 | else if value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN' 412 | else if value in UNARY then tag = 'UNARY' 413 | else if value in UNARY_MATH then tag = 'UNARY_MATH' 414 | else if value in SHIFT then tag = 'SHIFT' 415 | else if value in LOGIC or value is '?' and prev?.spaced then tag = 'LOGIC' 416 | else if prev and not prev.spaced 417 | if value is '(' and prev[0] in CALLABLE and not prev.stringEnd and not prev.regexEnd 418 | prev[0] = 'FUNC_EXIST' if prev[0] is '?' 419 | tag = 'CALL_START' 420 | else if value is '[' and prev[0] in INDEXABLE 421 | tag = 'INDEX_START' 422 | switch prev[0] 423 | when '?' then prev[0] = 'INDEX_SOAK' 424 | token = @makeToken tag, value 425 | switch value 426 | when '(', '{', '[' then @ends.push {tag: INVERSES[value], origin: token} 427 | when ')', '}', ']' then @pair value 428 | @tokens.push token 429 | value.length 430 | 431 | # Token Manipulators 432 | # ------------------ 433 | 434 | # A source of ambiguity in our grammar used to be parameter lists in function 435 | # definitions versus argument lists in function calls. Walk backwards, tagging 436 | # parameters specially in order to make things easier for the parser. 437 | tagParameters: -> 438 | return this if @tag() isnt ')' 439 | stack = [] 440 | {tokens} = this 441 | i = tokens.length 442 | tokens[--i][0] = 'PARAM_END' 443 | while tok = tokens[--i] 444 | switch tok[0] 445 | when ')' 446 | stack.push tok 447 | when '(', 'CALL_START' 448 | if stack.length then stack.pop() 449 | else if tok[0] is '(' 450 | tok[0] = 'PARAM_START' 451 | return this 452 | else return this 453 | this 454 | 455 | # Close up all remaining open blocks at the end of the file. 456 | closeIndentation: -> 457 | @outdentToken @indent 458 | 459 | # Match the contents of a delimited token and expand variables and expressions 460 | # inside it using Ruby-like notation for substitution of arbitrary 461 | # expressions. 462 | # 463 | # "Hello #{name.capitalize()}." 464 | # 465 | # If it encounters an interpolation, this method will recursively create a new 466 | # Lexer and tokenize until the `{` of `#{` is balanced with a `}`. 467 | # 468 | # - `str` is the start of the token contents (with the starting delimiter 469 | # stripped off.) 470 | # - `regex` matches the contents of a token (but not `end`, and not `#{` if 471 | # interpolations are desired). 472 | # - `end` is the terminator of the token. 473 | # - `offsetInChunk` is the start of the interpolated string in the current 474 | # chunk, including the starting delimiter. 475 | # 476 | # Examples of delimiters are `'`, `"`, `'''`, `"""` and `///`. 477 | # 478 | # This method allows us to have strings within interpolations within strings, 479 | # ad infinitum. 480 | matchWithInterpolations: (str, regex, end, offsetInChunk) -> 481 | tokens = [] 482 | loop 483 | [strPart] = regex.exec str 484 | 485 | # Push a fake 'NEOSTRING' token, which will get turned into a real string later. 486 | tokens.push @makeToken 'NEOSTRING', strPart, offsetInChunk 487 | 488 | str = str[strPart.length..] 489 | offsetInChunk += strPart.length 490 | 491 | break unless str[...2] is '#{' 492 | 493 | # The `1`s are to remove the `#` in `#{`. 494 | [line, column] = @getLineAndColumnFromChunk offsetInChunk + 1 495 | {tokens: nested, index} = 496 | new Lexer().tokenize str[1..], line: line, column: column, untilBalanced: on 497 | # Skip the trailing `}`. 498 | index += 1 499 | 500 | # Turn the leading and trailing `{` and `}` into parentheses. Unnecessary 501 | # parentheses will be removed later. 502 | [open, ..., close] = nested 503 | open[0] = open[1] = '(' 504 | close[0] = close[1] = ')' 505 | close.origin = ['', 'end of interpolation', close[2]] 506 | 507 | # Remove leading 'TERMINATOR' (if any). 508 | nested.splice 1, 1 if nested[1]?[0] is 'TERMINATOR' 509 | 510 | # Push a fake 'TOKENS' token, which will get turned into real tokens later. 511 | tokens.push ['TOKENS', nested] 512 | 513 | str = str[index..] 514 | offsetInChunk += index 515 | 516 | unless str[...end.length] is end 517 | @error "missing #{end}" 518 | 519 | {tokens, index: offsetInChunk + end.length} 520 | 521 | # Merge the array `tokens` of the fake token types 'TOKENS' and 'NEOSTRING' 522 | # (as returned by `matchWithInterpolations`) into the token stream. The value 523 | # of 'NEOSTRING's are converted using `fn` and turned into strings using 524 | # `quote` first. The tokens are wrapped in parentheses if needed, using 525 | # `start` and `end` for their location data. 526 | mergeInterpolationTokens: (tokens, {quote, start, end}, fn) -> 527 | if interpolated = tokens.length > 1 528 | errorToken = @makeToken '', 'interpolation', start + tokens[0][1].length, 2 529 | @token '(', '(', 0, 0, errorToken 530 | 531 | firstIndex = @tokens.length 532 | for token, i in tokens 533 | [tag, value] = token 534 | switch tag 535 | when 'TOKENS' 536 | # Optimize out empty interpolations (an empty pair of parentheses). 537 | continue if value.length is 2 538 | # Push all the tokens in the fake 'TOKENS' token. These already have 539 | # sane location data. 540 | locationToken = value[0] 541 | tokensToPush = value 542 | when 'NEOSTRING' 543 | # Convert 'NEOSTRING' into 'STRING'. 544 | converted = fn token[1], i 545 | # Optimize out empty strings. We ensure that the tokens stream always 546 | # starts with a string token, though, to make sure that the result 547 | # really is a string. 548 | if converted.length is 0 549 | if i is 0 550 | firstEmptyStringIndex = @tokens.length 551 | else 552 | continue 553 | # However, there is one case where we can optimize away a starting 554 | # empty string. 555 | if i is 2 and firstEmptyStringIndex? 556 | @tokens.splice firstEmptyStringIndex, 2 # Remove empty string and the plus. 557 | token[0] = 'STRING' 558 | token[1] = @makeString converted, quote 559 | locationToken = token 560 | tokensToPush = [token] 561 | if @tokens.length > firstIndex 562 | # Create a 0-length "+" token. 563 | plusToken = @token '+', '+' 564 | plusToken[2] = 565 | first_line: locationToken[2].first_line 566 | first_column: locationToken[2].first_column 567 | last_line: locationToken[2].first_line 568 | last_column: locationToken[2].first_column 569 | @tokens.push tokensToPush... 570 | 571 | if interpolated 572 | rparen = @token ')', ')', end, 0 573 | rparen.stringEnd = true 574 | 575 | # Pairs up a closing token, ensuring that all listed pairs of tokens are 576 | # correctly balanced throughout the course of the token stream. 577 | pair: (tag) -> 578 | unless tag is wanted = last(@ends)?.tag 579 | @error "unmatched #{tag}" unless 'OUTDENT' is wanted 580 | # Auto-close INDENT to support syntax like this: 581 | # 582 | # el.click((event) -> 583 | # el.hide()) 584 | # 585 | @outdentToken last(@indents), true 586 | return @pair tag 587 | @ends.pop() 588 | 589 | # Helpers 590 | # ------- 591 | 592 | # Returns the line and column number from an offset into the current chunk. 593 | # 594 | # `offset` is a number of characters into @chunk. 595 | getLineAndColumnFromChunk: (offset) -> 596 | if offset is 0 597 | return [@chunkLine, @chunkColumn] 598 | 599 | if offset >= @chunk.length 600 | string = @chunk 601 | else 602 | string = @chunk[..offset-1] 603 | 604 | lineCount = count string, '\n' 605 | 606 | column = @chunkColumn 607 | if lineCount > 0 608 | lines = string.split '\n' 609 | column = last(lines).length 610 | else 611 | column += string.length 612 | 613 | [@chunkLine + lineCount, column] 614 | 615 | # Same as "token", exception this just returns the token without adding it 616 | # to the results. 617 | makeToken: (tag, value, offsetInChunk = 0, length = value.length) -> 618 | locationData = {} 619 | [locationData.first_line, locationData.first_column] = 620 | @getLineAndColumnFromChunk offsetInChunk 621 | 622 | # Use length - 1 for the final offset - we're supplying the last_line and the last_column, 623 | # so if last_column == first_column, then we're looking at a character of length 1. 624 | lastCharacter = Math.max 0, length - 1 625 | [locationData.last_line, locationData.last_column] = 626 | @getLineAndColumnFromChunk offsetInChunk + lastCharacter 627 | 628 | token = [tag, value, locationData] 629 | 630 | token 631 | 632 | # Add a token to the results. 633 | # `offset` is the offset into the current @chunk where the token starts. 634 | # `length` is the length of the token in the @chunk, after the offset. If 635 | # not specified, the length of `value` will be used. 636 | # 637 | # Returns the new token. 638 | token: (tag, value, offsetInChunk, length, origin) -> 639 | token = @makeToken tag, value, offsetInChunk, length 640 | token.origin = origin if origin 641 | @tokens.push token 642 | token 643 | 644 | # Peek at a tag in the current token stream. 645 | tag: (index, tag) -> 646 | (tok = last @tokens, index) and if tag then tok[0] = tag else tok[0] 647 | 648 | # Peek at a value in the current token stream. 649 | value: (index, val) -> 650 | (tok = last @tokens, index) and if val then tok[1] = val else tok[1] 651 | 652 | # Are we in the midst of an unfinished expression? 653 | unfinished: -> 654 | LINE_CONTINUER.test(@chunk) or 655 | @tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-', 'YIELD', 656 | '**', 'SHIFT', 'RELATION', 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS'] 657 | 658 | formatString: (str) -> 659 | # Ignore escaped backslashes and remove escaped newlines. 660 | str.replace /\\[^\S\n]*(\n|\\)\s*/g, (escaped, character) -> 661 | if character is '\n' then '' else escaped 662 | 663 | formatHeregex: (str) -> 664 | str.replace(HEREGEX_OMIT, '$1$2').replace(MULTILINER, '\\n') 665 | 666 | # Constructs a string token by escaping quotes. 667 | makeString: (body, quote) -> 668 | return quote + quote unless body 669 | # Ignore escaped backslashes and unescape quotes. 670 | if match = OCTAL_ESCAPE.exec body 671 | @error "octal escape sequences are not allowed #{match[2]}", match.index + match[1].length + 1 672 | quote + body + quote 673 | 674 | # Throws a compiler error on the current position. 675 | error: (message, offset = 0) -> 676 | # TODO: Are there some cases we could improve the error line number by 677 | # passing the offset in the chunk where the error happened? 678 | [first_line, first_column] = @getLineAndColumnFromChunk offset 679 | throwSyntaxError message, {first_line, first_column} 680 | 681 | # Constants 682 | # --------- 683 | 684 | # Keywords that CoffeeScript shares in common with JavaScript. 685 | JS_KEYWORDS = [ 686 | 'true', 'false', 'null', 'this' 687 | 'new', 'delete', 'typeof', 'in', 'instanceof' 688 | 'return', 'throw', 'break', 'continue', 'debugger', 'yield' 689 | 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally' 690 | 'class', 'extends', 'super' 691 | ] 692 | 693 | # CoffeeScript-only keywords. 694 | COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'] 695 | 696 | COFFEE_ALIAS_MAP = 697 | and : '&&' 698 | or : '||' 699 | is : '==' 700 | isnt : '!=' 701 | not : '!' 702 | yes : 'true' 703 | no : 'false' 704 | on : 'true' 705 | off : 'false' 706 | 707 | COFFEE_ALIASES = (key for key of COFFEE_ALIAS_MAP) 708 | COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat COFFEE_ALIASES 709 | 710 | # The list of keywords that are reserved by JavaScript, but not used, or are 711 | # used by CoffeeScript internally. We throw an error when these are encountered, 712 | # to avoid having a JavaScript error at runtime. 713 | RESERVED = [ 714 | 'case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum' 715 | 'export', 'import', 'native', 'implements', 'interface', 'package', 'private' 716 | 'protected', 'public', 'static' 717 | ] 718 | 719 | STRICT_PROSCRIBED = ['arguments', 'eval', 'yield*'] 720 | 721 | # The superset of both JavaScript keywords and reserved words, none of which may 722 | # be used as identifiers or properties. 723 | JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED) 724 | 725 | exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED) 726 | exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED 727 | 728 | # The character code of the nasty Microsoft madness otherwise known as the BOM. 729 | BOM = 65279 730 | 731 | # Token matching regexes. 732 | IDENTIFIER = /// ^ 733 | (?!\d) 734 | ( (?: (?!\s)[$\w\x7f-\uffff] )+ ) 735 | ( [^\n\S]* : (?!:) )? # Is this a property name? 736 | /// 737 | 738 | NUMBER = /// 739 | ^ 0b[01]+ | # binary 740 | ^ 0o[0-7]+ | # octal 741 | ^ 0x[\da-f]+ | # hex 742 | ^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal 743 | ///i 744 | 745 | OPERATOR = /// ^ ( 746 | ?: [-=]> # function 747 | | [-+*/%<>&|^!?=]= # compound assign / compare 748 | | >>>=? # zero-fill right shift 749 | | ([-+:])\1 # doubles 750 | | ([&|<>*/%])\2=? # logic / shift / power / floor division / modulo 751 | | \?(\.|::) # soak access 752 | | \.{2,3} # range or splat 753 | ) /// 754 | 755 | WHITESPACE = /^[^\n\S]+/ 756 | 757 | COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/ 758 | 759 | CODE = /^[-=]>/ 760 | 761 | MULTI_DENT = /^(?:\n[^\n\S]*)+/ 762 | 763 | JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/ 764 | 765 | # String-matching-regexes. 766 | STRING_START = /^(?:'''|"""|'|")/ 767 | 768 | STRING_SINGLE = /// ^(?: [^\\'] | \\[\s\S] )* /// 769 | STRING_DOUBLE = /// ^(?: [^\\"#] | \\[\s\S] | \#(?!\{) )* /// 770 | 771 | STRING_OMIT = /\s*\n\s*/g 772 | HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g 773 | 774 | # Regex-matching-regexes. 775 | REGEX = /// ^ 776 | 777 | REGEX_FLAGS = /^\w*/ 778 | VALID_FLAGS = /^(?!.*(.).*\1)[imgy]*$/ 779 | 780 | HEREGEX = /// ^(?: [^\\/#] | \\[\s\S] | /(?!//) | \#(?!\{) )* /// 781 | 782 | HEREGEX_OMIT = /// 783 | ((?:\\\\)+) # consume (and preserve) an even number of backslashes 784 | | \\(\s|/) # preserve escaped whitespace and "de-escape" slashes 785 | | \s+(?:#.*)? # remove whitespace and comments 786 | ///g 787 | 788 | REGEX_ILLEGAL = /// ^ ( / | /{3}\s*) (\*) /// 789 | 790 | POSSIBLY_DIVISION = /// ^ /=?\s /// 791 | 792 | # Other regexes. 793 | MULTILINER = /\n/g 794 | 795 | HERECOMMENT_ILLEGAL = /\*\// 796 | 797 | LINE_CONTINUER = /// ^ \s* (?: , | \??\.(?![.\d]) | :: ) /// 798 | 799 | OCTAL_ESCAPE = /// ^ ((?: \\. | [^\\] )*) (\\ (?: 0[0-7] | [1-7] )) /// 800 | 801 | LEADING_BLANK_LINE = /^[^\n\S]*\n/ 802 | TRAILING_BLANK_LINE = /\n[^\n\S]*$/ 803 | 804 | TRAILING_SPACES = /\s+$/ 805 | 806 | # Compound assignment tokens. 807 | COMPOUND_ASSIGN = [ 808 | '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=' 809 | '&=', '^=', '|=', '**=', '//=', '%%=' 810 | ] 811 | 812 | # Unary tokens. 813 | UNARY = ['NEW', 'TYPEOF', 'DELETE', 'DO'] 814 | 815 | UNARY_MATH = ['!', '~'] 816 | 817 | # Logical tokens. 818 | LOGIC = ['&&', '||', '&', '|', '^'] 819 | 820 | # Bit-shifting tokens. 821 | SHIFT = ['<<', '>>', '>>>'] 822 | 823 | # Comparison tokens. 824 | COMPARE = ['==', '!=', '<', '>', '<=', '>='] 825 | 826 | # Mathematical tokens. 827 | MATH = ['*', '/', '%', '//', '%%'] 828 | 829 | # Relational tokens that are negatable with `not` prefix. 830 | RELATION = ['IN', 'OF', 'INSTANCEOF'] 831 | 832 | # Boolean tokens. 833 | BOOL = ['TRUE', 'FALSE'] 834 | 835 | # Tokens which could legitimately be invoked or indexed. An opening 836 | # parentheses or bracket following these tokens will be recorded as the start 837 | # of a function invocation or indexing operation. 838 | CALLABLE = ['IDENTIFIER', ')', ']', '?', '@', 'THIS', 'SUPER'] 839 | INDEXABLE = CALLABLE.concat ['NUMBER', 'STRING', 'REGEX', 'BOOL', 'NULL', 'UNDEFINED', '}', '::'] 840 | 841 | # Tokens which a regular expression will never immediately follow (except spaced 842 | # CALLABLEs in some cases), but which a division operator can. 843 | # 844 | # See: http://www-archive.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions 845 | NOT_REGEX = INDEXABLE.concat ['++', '--'] 846 | 847 | # Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN` 848 | # occurs at the start of a line. We disambiguate these from trailing whens to 849 | # avoid an ambiguity in the grammar. 850 | LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'] 851 | 852 | # Additional indent in front of these is ignored. 853 | INDENTABLE_CLOSERS = [')', '}', ']'] --------------------------------------------------------------------------------