├── .gitignore ├── CNAME ├── Dockerfile ├── LICENSE ├── README.md ├── bootstrap.html ├── css ├── bootstrap-responsive.css ├── bootstrap-responsive.min.css ├── bootstrap.css ├── bootstrap.min.css ├── cupertino │ ├── images │ │ ├── background.jpg │ │ ├── ui-bg_diagonals-thick_90_eeeeee_40x40.png │ │ ├── ui-bg_flat_15_cd0a0a_40x100.png │ │ ├── ui-bg_glass_100_e4f1fb_1x400.png │ │ ├── ui-bg_glass_50_3baae3_1x400.png │ │ ├── ui-bg_glass_80_d7ebf9_1x400.png │ │ ├── ui-bg_highlight-hard_100_f2f5f7_1x100.png │ │ ├── ui-bg_highlight-hard_70_000000_1x100.png │ │ ├── ui-bg_highlight-soft_100_deedf7_1x100.png │ │ ├── ui-bg_highlight-soft_25_ffef8f_1x100.png │ │ ├── ui-icons_2694e8_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_3d80b3_256x240.png │ │ ├── ui-icons_72a7cf_256x240.png │ │ ├── ui-icons_ffffff_256x240.png │ │ └── uiTabsArrow.png │ ├── jquery-ui-1.9.0.custom.css │ └── jquery-ui-1.9.0.custom.min.css └── style.css ├── dublin.rdf ├── favicon.ico ├── img └── logo.png ├── index.html ├── js ├── Worker.js ├── analyzer.js ├── gCodeReader.js ├── renderer.js ├── renderer3d.js └── ui.js ├── lib ├── TrackballControls.js ├── angular │ ├── angular-bootstrap-prettify.js │ ├── angular-bootstrap-prettify.min.js │ ├── angular-bootstrap.js │ ├── angular-bootstrap.min.js │ ├── angular-cookies.js │ ├── angular-cookies.min.js │ ├── angular-loader.js │ ├── angular-loader.min.js │ ├── angular-mocks.js │ ├── angular-resource.js │ ├── angular-resource.min.js │ ├── angular-sanitize.js │ ├── angular-sanitize.min.js │ ├── angular-scenario.js │ ├── angular.js │ ├── angular.min.js │ ├── jstd-scenario-adapter-config.js │ └── jstd-scenario-adapter.js ├── bootstrap.js ├── bootstrap.min.js ├── codemirror.css ├── codemirror.js ├── jquery-1.8.2.js ├── jquery-1.8.2.min.js ├── jquery-ui-1.9.0.custom.js ├── mode_gcode │ └── gcode_mode.js ├── modernizr.custom.09684.js ├── three.js ├── three.min.js └── zlib.min.js ├── robots.txt └── sitemap.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | gcode.ws -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | COPY . /usr/share/nginx/html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | gCodeViewer by hudbrog is licensed under a Creative Commons Attribution 4.0 International License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gCodeVisualizer - a web-based visual gcode viewer and analyzer. 2 | Available online at http://gcode.ws/ 3 | 4 | If you want to use it locally with chrome - you will need to add "--allow-file-access-from-files" command line parameter, 5 | which is unsafe (and you should never browse internet with that option "on"). 6 | 7 | ### Docker Setup Instructions: 8 | 9 | 1. Build docker image 10 | ``` bash 11 | docker build -t gcodews . 12 | ``` 13 | 14 | 2. Run docker image 15 | ``` bash 16 | docker docker run --name gcodews -d -p 8080:80 gcodews 17 | ``` 18 | 19 | 3. Access site via port `8080` on your browser 20 | ``` 21 | localhost:8080 22 | 127.0.0.1:8080 23 | X.X.X.X:8080 24 | :8080 25 | ``` -------------------------------------------------------------------------------- /bootstrap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 |
34 |
35 |
36 | 37 |
38 |
39 | 44 |
45 |
46 | 47 | 48 | 49 | 50 |
Drop file here
51 |
52 |
53 |
54 |
55 | 60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 79 |
80 |
81 |

82 |
83 |
84 |
85 |
86 | 91 |
92 |
93 |

94 |
95 |
96 |
97 |
98 | 103 |
104 |
105 | Show non-extrusion moves
106 | Show retracts and restarts
107 | Move model to the center of the grid
108 | Show different speeds with different colors
109 |
110 |
111 |
112 | 113 |
114 | 119 |
120 |
121 | These require re-analyzing file:
122 | Sort layers by Z
123 | Hide empty layers
124 | Analyze size and filament usage
125 |
126 |
127 |
128 | 129 |
130 | 131 | 132 | 133 |
134 |
135 |
136 | 141 |
142 |
143 | 144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.1.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade.in{top:auto}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:block;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /css/cupertino/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/background.jpg -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_flat_15_cd0a0a_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_flat_15_cd0a0a_40x100.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_glass_100_e4f1fb_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_glass_100_e4f1fb_1x400.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_glass_50_3baae3_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_glass_50_3baae3_1x400.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_glass_80_d7ebf9_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_glass_80_d7ebf9_1x400.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_highlight-hard_70_000000_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_highlight-hard_70_000000_1x100.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_highlight-soft_100_deedf7_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_highlight-soft_100_deedf7_1x100.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-bg_highlight-soft_25_ffef8f_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-bg_highlight-soft_25_ffef8f_1x100.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-icons_2694e8_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-icons_2694e8_256x240.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-icons_3d80b3_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-icons_3d80b3_256x240.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-icons_72a7cf_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-icons_72a7cf_256x240.png -------------------------------------------------------------------------------- /css/cupertino/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /css/cupertino/images/uiTabsArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/css/cupertino/images/uiTabsArrow.png -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | margin:0; 4 | padding:0; 5 | color:#000; 6 | background-image: url(cupertino/images/background.jpg); 7 | } 8 | #wrap { 9 | width:1000px; 10 | height: 680px; 11 | position: relative; 12 | margin: 20px auto; 13 | background:#ffffff; 14 | /*border: 5px outset #bbb;*/ 15 | -moz-border-radius: 10px; 16 | -webkit-border-radius: 10px; 17 | border-radius: 10px; 18 | } 19 | #control { 20 | float:left; 21 | width:300px; 22 | margin-top: 10px; 23 | margin-left: 10px; 24 | /*background:#ffffff;*/ 25 | /*margin-right: 10px;*/ 26 | /*height: 680px;*/ 27 | } 28 | #file_block { 29 | /*margin-top: 20px;*/ 30 | /*margin-left: 20px;*/ 31 | } 32 | 33 | #control_bottom { 34 | position: absolute; 35 | bottom: 0; 36 | width: 300px; 37 | margin-bottom: 10px; 38 | } 39 | #options{ 40 | float: left; 41 | margin-left: 5px; 42 | margin-bottom: 5px; 43 | } 44 | #gcode { 45 | background: #ffffff; 46 | float:right; 47 | width: 680px; 48 | height: 680px; 49 | border-width: 2px; 50 | border-style: none none none solid; 51 | -webkit-border-top-right-radius: 10px; 52 | -webkit-border-bottom-right-radius: 10px; 53 | -moz-border-radius-topright: 10px; 54 | -moz-border-radius-bottomright: 10px; 55 | border-top-right-radius: 10px; 56 | border-bottom-right-radius: 10px;} 57 | 58 | .CodeMirror { 59 | border-bottom-right-radius: 10px; 60 | } 61 | 62 | #canvas{ 63 | /*float: left;*/ 64 | clear:none; 65 | } 66 | 67 | #drop_zone { 68 | border: 2px dashed #bbb; 69 | -moz-border-radius: 5px; 70 | -webkit-border-radius: 5px; 71 | border-radius: 5px; 72 | padding: 40px; 73 | margin: 5px; 74 | text-align: center; 75 | font: 20pt bold; 76 | color: #bbb 77 | } 78 | #slider-vertical { 79 | /*position: absolute;*/ 80 | /*right: 0;*/ 81 | /*top: 0;*/ 82 | height: 630px; 83 | width: 10px; 84 | float: right; 85 | margin-right: 10px; 86 | } 87 | #slider-horizontal { 88 | height: 10px; 89 | width: 640px; 90 | position: absolute; 91 | bottom: 0; 92 | margin-bottom: 5px; 93 | } 94 | 95 | #main_button_block { 96 | /*float: right;*/ 97 | margin-bottom: 5px; 98 | margin-right: 5px; 99 | } 100 | 101 | .mbut{ 102 | height:60px; 103 | width: 250px; 104 | font-size: 1em; 105 | } 106 | 107 | #tabs-min { 108 | height: 680px; 109 | padding: 0px; 110 | } 111 | 112 | .mtab{ 113 | background: transparent; 114 | border: none; 115 | font-weight: normal; 116 | font-size: 0.5em; 117 | } 118 | 119 | .mtab-defstate{ 120 | background: transparent; 121 | border: none; 122 | 123 | } 124 | 125 | .mtab-content{ 126 | background: #ffffff; 127 | background-image: none; 128 | border: 0px none #dddddd; 129 | border-bottom-style: none; 130 | margin: 0px; 131 | } 132 | 133 | .bar { 134 | -webkit-transition: width 0s linear !important; 135 | -moz-transition: width 0s linear !important; 136 | -o-transition: width 0s linear !important; 137 | transition: width 0s linear !important; 138 | } 139 | 140 | .nav { 141 | margin-bottom: 0px !important; 142 | } 143 | 144 | .tab-content { 145 | overflow: visible; 146 | } 147 | 148 | .aboutpage { 149 | margin: 10px; 150 | } 151 | 152 | #accordion_top .ui-accordion-content{ 153 | padding: 5px; 154 | } 155 | 156 | #tabs-min .ui-tabs-panel { 157 | padding: 0px; 158 | } 159 | 160 | #progressBlock { 161 | margin-left: 10px; 162 | margin-right: 10px; 163 | margin-bottom: 10px; 164 | } 165 | 166 | .colorBox { 167 | width: 50px; 168 | height: 15px; 169 | border: 1px solid #000000; 170 | float:left; 171 | } 172 | 173 | .activeline {background: #fff0b6 !important;} 174 | 175 | .socials { 176 | padding: 10px; 177 | } 178 | 179 | H1 { text-align: center } -------------------------------------------------------------------------------- /dublin.rdf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | hudbrog 5 | 2013-02-01 6 | gcode viewer - gCodeViewer is a visual G-Code visualizer, viewer and analyzer online in your own browser! 7 | EN 8 | hudbrog 9 | http://gcode.ws/ 10 | 11 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/favicon.ico -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudbrog/gCodeViewer/853fe00fe5a639b578e5d6c0427647626deb44d4/img/logo.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | gcode viewer - online gcode viewer and analyzer in your browser! 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
60 | 67 | 68 | 81 | 82 | 111 | 112 | 113 |
114 |
115 | gCodeViewer - an online gcode viewer 116 | 117 |
118 |
119 | 124 |
125 |
126 | 127 | 128 | 129 | 130 |
Drop file here
131 |
132 |
133 |
134 |
135 | 140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | 159 |
160 |
161 |

162 |
163 |
164 |
165 |
166 | 171 |
172 |
173 |

174 |
175 |
176 |
177 |
178 | 183 |
184 |
185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 |
198 |
199 |
200 | 201 |
202 | 207 |
208 |
209 | These require re-analyzing file:
210 | 211 | 212 | 213 |
214 |
215 |
216 | 217 |
218 | 223 |
224 |
225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 |
234 |
235 |
236 | 237 |
238 | 243 |
244 |
245 | 246 |
247 | 248 |
249 |
250 |
251 |
252 | 253 | 254 |
255 | 256 | 257 | 258 |
259 |
260 |
261 | 267 |
268 |
269 | 270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |

gCodeViewer

282 | gCodeViewer is a visual GCode visualizer, viewer and analyzer in your own browser! It works on any OS in almost any modern browser (chrome, ff, safari 6, opera, ie10 should work too). All you need to do - is drag your *.gcode file to the designated zone.

283 | Current features include:
284 |
    285 |
  • Visualize GCode in 2D, layer by layer 286 |
      287 |
    • Show retracts and restarts
    • 288 |
    • Show print/move/retract speeds
    • 289 |
    • Display only part of layer, animate sequence of layer printing
    • 290 |
    • Show two layers simultaneously so you can check overhangs
    • 291 |
    • Adjust line width to simulate print more closely
    • 292 |
    • Gcode viewer will try to parse nozzle and filament diameters from gcode, but it may fail to do so. In that case you will need to set it manually in 'Printer Info' tab
    • 293 |
    294 |
  • 295 |
  • Analyze GCode 296 |
      297 |
    • Print time, amount of plastic used, layer height, etc. for whole file and for single layer
    • 298 |
    • Reference visualized part to GCode file (i.e. select a certain part of 2d visualization, switch to GCode view - it will highlight list of lines responsible for visualized piece)
    • 299 |
    300 |
  • 301 |
  • Multiplatform, available online (but works offline too), works locally (doesn't upload you g-code anywhere or download anything but application itself)
  • 302 |
  • 3D visualization (I don't know what for, it's dull and useless.. need to rewrite it from scratch)
  • 303 |
  • And yeah, it's slow, memory hungry and only supports modern browsers
  • 304 |
  • Totally open source so you can use it as you like
  • 305 |
306 |

307 | All sources are available at https://github.com/hudbrog/gCodeViewer
308 | If you find any bugs or have feature requests - don't hesitate to post them to https://github.com/hudbrog/gCodeViewer/issues
309 | And I would appreciate if you like it on Thingiverse page: http://www.thingiverse.com/thing:35248
310 |
311 | 312 |
313 |
314 |
315 |
316 |
317 | 320 | 321 | 328 | 329 | 330 | 331 | -------------------------------------------------------------------------------- /js/analyzer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created with JetBrains WebStorm. 3 | * User: hudbrog 4 | * Date: 2/14/13 5 | * Time: 1:15 PM 6 | */ 7 | 8 | GCODE.analyzer = (function(){ 9 | var savedRenderOptions, dia; 10 | var canvas, ctx, transform; 11 | var lastAnalyzedLayer=-1; 12 | var initialized = false; 13 | var usableLevel = 190, stepLevel=30; 14 | 15 | var saveOptions = function(){ 16 | savedRenderOptions = $.extend({}, GCODE.renderer.getOptions()); 17 | // console.log(savedRenderOptions); 18 | } 19 | 20 | var restoreOptions = function(){ 21 | // for(var opt in savedRenderOptions){ 22 | // if(savedRenderOptions.hasOwnProperty(opt)){ 23 | // var par = {} 24 | // GCODE.renderer.setOption({opt: savedRenderOptions[opt]}); 25 | // console.log(opt + ": " + savedRenderOptions[opt]) 26 | // } 27 | // }; 28 | GCODE.renderer.setOption(savedRenderOptions); 29 | // console.log(GCODE.renderer.getOptions()); 30 | initialized=false; 31 | } 32 | 33 | var prepareOptions = function(){ 34 | GCODE.renderer.setOption({showMoves: false}); 35 | GCODE.renderer.setOption({showRetracts: false}); 36 | GCODE.renderer.setOption({alpha: false}); 37 | GCODE.renderer.setOption({actualWidth: true}); 38 | GCODE.renderer.setOption({differentiateColors: false}); 39 | GCODE.renderer.setOption({showNextLayer: false}); 40 | GCODE.renderer.setOption({colorGrid: "#ffffff"}); 41 | GCODE.renderer.setOption({renderAnalysis: true}); 42 | // console.log(GCODE.renderer.getOptions()); 43 | } 44 | 45 | var multiplyPoint = function(point) { 46 | return { 47 | x: parseInt(point.x * transform.a + point.y * transform.c + transform.e), 48 | y: parseInt(point.x * transform.b + point.y * transform.d + transform.f) 49 | } 50 | } 51 | 52 | var checkPoint = function(imgData, p) { 53 | if (imgData.data[(p.x+ p.y * imgData.width) * 4+3] > 100) { 54 | return imgData.data[(p.x+ p.y * imgData.width) * 4]; 55 | } else { 56 | return 255; 57 | } 58 | 59 | } 60 | 61 | var checkArea = function(imgData, pnt) { 62 | var r = parseInt(dia/2)+1; 63 | var res = 0, p = multiplyPoint(pnt); 64 | 65 | res += checkPoint(imgData, {x: p.x , y: p.y }); 66 | res += checkPoint(imgData, {x: p.x+r, y: p.y }); 67 | res += checkPoint(imgData, {x: p.x-r, y: p.y }); 68 | res += checkPoint(imgData, {x: p.x , y: p.y+r}); 69 | res += checkPoint(imgData, {x: p.x , y: p.y-r}); 70 | r = parseInt(r*0.7)===0?1:parseInt(r*0.7); 71 | res += checkPoint(imgData, {x: p.x+r, y: p.y-r}); 72 | res += checkPoint(imgData, {x: p.x+r, y: p.y+r}); 73 | res += checkPoint(imgData, {x: p.x-r, y: p.y-r}); 74 | res += checkPoint(imgData, {x: p.x-r, y: p.y+r}); 75 | 76 | res = res/9; 77 | 78 | return res; 79 | // return checkPoint(imgData, p)===1?true:false; 80 | } 81 | 82 | 83 | var findNearestPoint = function(imgData, x,y,lastX,lastY){ 84 | var len = [], stepCnt = [], step = []; 85 | var p0 = multiplyPoint({x:lastX, y:lastY}); 86 | var p = multiplyPoint({x:x,y:y}); 87 | len[0] = parseInt(p.x - p0.x); 88 | len[1] = parseInt(p.y - p0.y); 89 | // len[2] = Math.sqrt(xlen*xlen + ylen*ylen); 90 | stepCnt[0] = Math.abs(len[0]/(dia*0.7)); 91 | stepCnt[1] = Math.abs(len[1]/(dia*0.7)); 92 | var index = Math.abs(len[0])'); 133 | // } 134 | var r2 = checkArea(imgData, {x: x, y: y}); 135 | var r1 = checkArea(imgData, {x: prevX, y: prevY}); 136 | var result = (r2+r1)/2; 137 | model[layerNum][i].errLevelE = r20 && result < 3){ 176 | // if(lastErrCmd == i-1) { 177 | // errStreak++; 178 | // }else{ 179 | // errStreak = 0; 180 | // } 181 | // totalErr++; 182 | // if(longestErrStreak < errStreak){ 183 | // longestErrStreak = errStreak; 184 | // } 185 | 186 | // ctx.strokeStyle = "#00ff00"; 187 | // ctx.fillStyle = "#00ff00"; 188 | // ctx.beginPath(); 189 | // ctx.arc(x, y, 0.2, 0, Math.PI*2, true); 190 | // ctx.stroke(); 191 | // ctx.fill(); 192 | } 193 | } 194 | } 195 | ctx.putImageData(imgData, 0, 0); 196 | } 197 | return {errors: totalErr, longestStreak: longestErrStreak}; 198 | } 199 | 200 | var analyze = function(layerNum){ 201 | GCODE.renderer.render(layerNum, 0, GCODE.renderer.getLayerNumSegments(layerNum)); 202 | return drawLineEnds(layerNum+1); 203 | } 204 | 205 | var analyzeStep = function(){ 206 | var numLayers = GCODE.renderer.getModelNumLayers(); 207 | var i = lastAnalyzedLayer; 208 | var result = analyze(i); 209 | var progress = 100/numLayers*i; 210 | $('#analysisProgress').width(parseInt(progress)+'%').text(parseInt(progress)+'%'); 211 | if(i 4){ 238 | //// lastAnalyzedLayer = i; 239 | //// return false; 240 | //// } 241 | // } 242 | // $('#analysisModal').modal('hide'); 243 | return true; 244 | } 245 | 246 | var init = function(){ 247 | if(initialized)return; 248 | canvas = document.getElementById('canvas'); 249 | ctx = canvas.getContext('2d'); 250 | transform = ctx.getTransform(); 251 | saveOptions(); 252 | prepareOptions(); 253 | var extrusionWidth = GCODE.renderer.getOptions()['extrusionWidth']; 254 | console.log("Extrusion width is " +extrusionWidth); 255 | var p1 = multiplyPoint({x:0, y:0}); 256 | var p2 = multiplyPoint({x: extrusionWidth, y: 0}); 257 | dia = Math.abs(Math.abs(p2.x) - Math.abs(p1.x)); 258 | console.log("width is " + dia); 259 | initialized=true; 260 | } 261 | 262 | return { 263 | runAnalyze: function(){ 264 | init(); 265 | analyzeCycle(); 266 | // restoreOptions(); 267 | }, 268 | analyzeOnce: function(layerNum){ 269 | init(); 270 | analyze(layerNum); 271 | }, 272 | restoreRenderer: function(){ 273 | restoreOptions(); 274 | }, 275 | multiplyPoint2: function(point) { 276 | return { 277 | x: parseInt(point.x * transform.a + point.y * transform.c + transform.e), 278 | y: parseInt(point.x * transform.b + point.y * transform.d + transform.f) 279 | } 280 | } 281 | } 282 | }()); -------------------------------------------------------------------------------- /js/gCodeReader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User: hudbrog (hudbrog@gmail.com) 3 | * Date: 10/21/12 4 | * Time: 7:31 AM 5 | */ 6 | 7 | GCODE.gCodeReader = (function(){ 8 | // ***** PRIVATE ****** 9 | var gcode, lines; 10 | var z_heights = {}; 11 | var model = []; 12 | var max = {x: undefined, y: undefined, z: undefined, speed: undefined, volSpeed: undefined, extrSpeed: undefined}; 13 | var min = {x: undefined, y: undefined, z: undefined, speed: undefined, volSpeed: undefined, extrSpeed: undefined}; 14 | var modelSize = {x: undefined, y: undefined, z: undefined}; 15 | var filamentByLayer = {}; 16 | var filamentByExtruder = {}; 17 | var printTimeByLayer; 18 | var totalFilament=0; 19 | var printTime=0; 20 | var totalWeight = 0; 21 | var layerHeight = 0; 22 | var layerCnt = 0; 23 | var layerTotal = 0; 24 | var speeds = {}; 25 | var slicer = 'unknown'; 26 | var speedsByLayer = {}; 27 | var volSpeeds = {}; 28 | var volSpeedsByLayer = {}; 29 | var extrusionSpeeds = {}; 30 | var extrusionSpeedsByLayer = {}; 31 | var gCodeOptions = { 32 | sortLayers: false, 33 | purgeEmptyLayers: true, 34 | analyzeModel: false, 35 | filamentType: "ABS", 36 | filamentDia: 1.75, 37 | nozzleDia: 0.4 38 | }; 39 | 40 | var prepareGCode = function(){ 41 | if(!lines)return; 42 | gcode = []; 43 | var i; 44 | for(i=0;i 1.0 ) { 106 | 107 | mouseOnBall.normalize(); 108 | 109 | } else { 110 | 111 | mouseOnBall.z = Math.sqrt( 1.0 - length * length ); 112 | 113 | } 114 | 115 | _eye.copy( _this.object.position ).subSelf( _this.target ); 116 | 117 | var projection = _this.object.up.clone().setLength( mouseOnBall.y ); 118 | projection.addSelf( _this.object.up.clone().crossSelf( _eye ).setLength( mouseOnBall.x ) ); 119 | projection.addSelf( _eye.setLength( mouseOnBall.z ) ); 120 | 121 | return projection; 122 | 123 | }; 124 | 125 | this.rotateCamera = function () { 126 | 127 | var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() ); 128 | 129 | if ( angle ) { 130 | 131 | var axis = ( new THREE.Vector3() ).cross( _rotateStart, _rotateEnd ).normalize(), 132 | quaternion = new THREE.Quaternion(); 133 | 134 | angle *= _this.rotateSpeed; 135 | 136 | quaternion.setFromAxisAngle( axis, -angle ); 137 | 138 | quaternion.multiplyVector3( _eye ); 139 | quaternion.multiplyVector3( _this.object.up ); 140 | 141 | quaternion.multiplyVector3( _rotateEnd ); 142 | 143 | if ( _this.staticMoving ) { 144 | 145 | _rotateStart.copy( _rotateEnd ); 146 | 147 | } else { 148 | 149 | quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) ); 150 | quaternion.multiplyVector3( _rotateStart ); 151 | 152 | } 153 | 154 | } 155 | 156 | }; 157 | 158 | this.zoomCamera = function () { 159 | 160 | var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; 161 | 162 | if ( factor !== 1.0 && factor > 0.0 ) { 163 | 164 | _eye.multiplyScalar( factor ); 165 | 166 | if ( _this.staticMoving ) { 167 | 168 | _zoomStart.copy( _zoomEnd ); 169 | 170 | } else { 171 | 172 | _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; 173 | 174 | } 175 | 176 | } 177 | 178 | }; 179 | 180 | this.panCamera = function () { 181 | 182 | var mouseChange = _panEnd.clone().subSelf( _panStart ); 183 | 184 | if ( mouseChange.lengthSq() ) { 185 | 186 | mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); 187 | 188 | var pan = _eye.clone().crossSelf( _this.object.up ).setLength( mouseChange.x ); 189 | pan.addSelf( _this.object.up.clone().setLength( mouseChange.y ) ); 190 | 191 | _this.object.position.addSelf( pan ); 192 | _this.target.addSelf( pan ); 193 | 194 | if ( _this.staticMoving ) { 195 | 196 | _panStart = _panEnd; 197 | 198 | } else { 199 | 200 | _panStart.addSelf( mouseChange.sub( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); 201 | 202 | } 203 | 204 | } 205 | 206 | }; 207 | 208 | this.checkDistances = function () { 209 | 210 | if ( !_this.noZoom || !_this.noPan ) { 211 | 212 | if ( _this.object.position.lengthSq() > _this.maxDistance * _this.maxDistance ) { 213 | 214 | _this.object.position.setLength( _this.maxDistance ); 215 | 216 | } 217 | 218 | if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) { 219 | 220 | _this.object.position.add( _this.target, _eye.setLength( _this.minDistance ) ); 221 | 222 | } 223 | 224 | } 225 | 226 | }; 227 | 228 | this.update = function () { 229 | 230 | _eye.copy( _this.object.position ).subSelf( _this.target ); 231 | 232 | if ( !_this.noRotate ) { 233 | 234 | _this.rotateCamera(); 235 | 236 | } 237 | 238 | if ( !_this.noZoom ) { 239 | 240 | _this.zoomCamera(); 241 | 242 | } 243 | 244 | if ( !_this.noPan ) { 245 | 246 | _this.panCamera(); 247 | 248 | } 249 | 250 | _this.object.position.add( _this.target, _eye ); 251 | 252 | _this.checkDistances(); 253 | 254 | _this.object.lookAt( _this.target ); 255 | 256 | if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) { 257 | 258 | _this.dispatchEvent( changeEvent ); 259 | 260 | lastPosition.copy( _this.object.position ); 261 | 262 | } 263 | 264 | }; 265 | 266 | // listeners 267 | 268 | function keydown( event ) { 269 | 270 | if ( ! _this.enabled ) return; 271 | 272 | window.removeEventListener( 'keydown', keydown ); 273 | 274 | _prevState = _state; 275 | 276 | if ( _state !== STATE.NONE ) { 277 | 278 | return; 279 | 280 | } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) { 281 | 282 | _state = STATE.ROTATE; 283 | 284 | } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) { 285 | 286 | _state = STATE.ZOOM; 287 | 288 | } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) { 289 | 290 | _state = STATE.PAN; 291 | 292 | } 293 | 294 | } 295 | 296 | function keyup( event ) { 297 | 298 | if ( ! _this.enabled ) return; 299 | 300 | _state = _prevState; 301 | 302 | window.addEventListener( 'keydown', keydown, false ); 303 | 304 | } 305 | 306 | function mousedown( event ) { 307 | 308 | if ( ! _this.enabled ) return; 309 | 310 | event.preventDefault(); 311 | event.stopPropagation(); 312 | 313 | if ( _state === STATE.NONE ) { 314 | 315 | _state = event.button; 316 | 317 | } 318 | 319 | if ( _state === STATE.ROTATE && !_this.noRotate ) { 320 | 321 | _rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY ); 322 | 323 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) { 324 | 325 | _zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY ); 326 | 327 | } else if ( _state === STATE.PAN && !_this.noPan ) { 328 | 329 | _panStart = _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY ); 330 | 331 | } 332 | 333 | document.addEventListener( 'mousemove', mousemove, false ); 334 | document.addEventListener( 'mouseup', mouseup, false ); 335 | 336 | } 337 | 338 | function mousemove( event ) { 339 | 340 | if ( ! _this.enabled ) return; 341 | 342 | if ( _state === STATE.ROTATE && !_this.noRotate ) { 343 | 344 | _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY ); 345 | 346 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) { 347 | 348 | _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY ); 349 | 350 | } else if ( _state === STATE.PAN && !_this.noPan ) { 351 | 352 | _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY ); 353 | 354 | } 355 | 356 | } 357 | 358 | function mouseup( event ) { 359 | 360 | if ( ! _this.enabled ) return; 361 | 362 | event.preventDefault(); 363 | event.stopPropagation(); 364 | 365 | _state = STATE.NONE; 366 | 367 | document.removeEventListener( 'mousemove', mousemove ); 368 | document.removeEventListener( 'mouseup', mouseup ); 369 | 370 | } 371 | 372 | function mousewheel( event ) { 373 | 374 | if ( ! _this.enabled ) return; 375 | 376 | event.preventDefault(); 377 | event.stopPropagation(); 378 | 379 | var delta = 0; 380 | 381 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 382 | 383 | delta = event.wheelDelta / 40; 384 | 385 | } else if ( event.detail ) { // Firefox 386 | 387 | delta = - event.detail / 3; 388 | 389 | } 390 | 391 | _zoomStart.y += ( 1 / delta ) * 0.05; 392 | 393 | } 394 | 395 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 396 | 397 | this.domElement.addEventListener( 'mousedown', mousedown, false ); 398 | this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); 399 | this.domElement.addEventListener( 'mousewheel', mousewheel, false ); 400 | 401 | window.addEventListener( 'keydown', keydown, false ); 402 | window.addEventListener( 'keyup', keyup, false ); 403 | 404 | this.handleResize(); 405 | 406 | }; 407 | -------------------------------------------------------------------------------- /lib/angular/angular-bootstrap-prettify.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.2 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(q,k,I){'use strict';function G(c){return c.replace(/\&/g,"&").replace(/\/g,">").replace(/"/g,""")}function D(c,e){var b=k.element("
"+e+"
");c.html("");c.append(b.contents());return c}var t={},w={value:{}},L={"angular.js":"http://code.angularjs.org/angular-"+k.version.full+".min.js","angular-resource.js":"http://code.angularjs.org/angular-resource-"+k.version.full+".min.js","angular-sanitize.js":"http://code.angularjs.org/angular-sanitize-"+k.version.full+ 7 | ".min.js","angular-cookies.js":"http://code.angularjs.org/angular-cookies-"+k.version.full+".min.js"};t.jsFiddle=function(c,e,b){return{terminal:!0,link:function(x,a,r){function d(a,b){return''}var H={html:"",css:"",js:""};k.forEach(r.jsFiddle.split(" "),function(a,b){var d=a.split(".")[1];H[d]+=d=="html"?b==0?"
\n"+c(a,2):"\n\n\n <\!-- CACHE FILE: "+a+' --\>\n 192 | 193 |
194 | 195 | 196 |
197 |
198 |

199 | 200 | {{item.actor.name}} 201 | Expand replies: {{item.links.replies[0].count}} 202 |

203 | {{item.object.content | html}} 204 |
205 | 206 | {{reply.actor.name}}: {{reply.content | html}} 207 |
208 |
209 |
210 | 211 | 212 | 213 | 214 | */ 215 | angular.module('ngResource', ['ng']). 216 | factory('$resource', ['$http', '$parse', function($http, $parse) { 217 | var DEFAULT_ACTIONS = { 218 | 'get': {method:'GET'}, 219 | 'save': {method:'POST'}, 220 | 'query': {method:'GET', isArray:true}, 221 | 'remove': {method:'DELETE'}, 222 | 'delete': {method:'DELETE'} 223 | }; 224 | var noop = angular.noop, 225 | forEach = angular.forEach, 226 | extend = angular.extend, 227 | copy = angular.copy, 228 | isFunction = angular.isFunction, 229 | getter = function(obj, path) { 230 | return $parse(path)(obj); 231 | }; 232 | 233 | /** 234 | * We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow 235 | * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path 236 | * segments: 237 | * segment = *pchar 238 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 239 | * pct-encoded = "%" HEXDIG HEXDIG 240 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 241 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 242 | * / "*" / "+" / "," / ";" / "=" 243 | */ 244 | function encodeUriSegment(val) { 245 | return encodeUriQuery(val, true). 246 | replace(/%26/gi, '&'). 247 | replace(/%3D/gi, '='). 248 | replace(/%2B/gi, '+'); 249 | } 250 | 251 | 252 | /** 253 | * This method is intended for encoding *key* or *value* parts of query component. We need a custom 254 | * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be 255 | * encoded per http://tools.ietf.org/html/rfc3986: 256 | * query = *( pchar / "/" / "?" ) 257 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 258 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 259 | * pct-encoded = "%" HEXDIG HEXDIG 260 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 261 | * / "*" / "+" / "," / ";" / "=" 262 | */ 263 | function encodeUriQuery(val, pctEncodeSpaces) { 264 | return encodeURIComponent(val). 265 | replace(/%40/gi, '@'). 266 | replace(/%3A/gi, ':'). 267 | replace(/%24/g, '$'). 268 | replace(/%2C/gi, ','). 269 | replace((pctEncodeSpaces ? null : /%20/g), '+'); 270 | } 271 | 272 | function Route(template, defaults) { 273 | this.template = template = template + '#'; 274 | this.defaults = defaults || {}; 275 | var urlParams = this.urlParams = {}; 276 | forEach(template.split(/\W/), function(param){ 277 | if (param && template.match(new RegExp("[^\\\\]:" + param + "\\W"))) { 278 | urlParams[param] = true; 279 | } 280 | }); 281 | this.template = template.replace(/\\:/g, ':'); 282 | } 283 | 284 | Route.prototype = { 285 | url: function(params) { 286 | var self = this, 287 | url = this.template, 288 | encodedVal; 289 | 290 | params = params || {}; 291 | forEach(this.urlParams, function(_, urlParam){ 292 | encodedVal = encodeUriSegment(params[urlParam] || self.defaults[urlParam] || ""); 293 | url = url.replace(new RegExp(":" + urlParam + "(\\W)"), encodedVal + "$1"); 294 | }); 295 | url = url.replace(/\/?#$/, ''); 296 | var query = []; 297 | forEach(params, function(value, key){ 298 | if (!self.urlParams[key]) { 299 | query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value)); 300 | } 301 | }); 302 | query.sort(); 303 | url = url.replace(/\/*$/, ''); 304 | return url + (query.length ? '?' + query.join('&') : ''); 305 | } 306 | }; 307 | 308 | 309 | function ResourceFactory(url, paramDefaults, actions) { 310 | var route = new Route(url); 311 | 312 | actions = extend({}, DEFAULT_ACTIONS, actions); 313 | 314 | function extractParams(data){ 315 | var ids = {}; 316 | forEach(paramDefaults || {}, function(value, key){ 317 | ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value; 318 | }); 319 | return ids; 320 | } 321 | 322 | function Resource(value){ 323 | copy(value || {}, this); 324 | } 325 | 326 | forEach(actions, function(action, name) { 327 | var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH'; 328 | Resource[name] = function(a1, a2, a3, a4) { 329 | var params = {}; 330 | var data; 331 | var success = noop; 332 | var error = null; 333 | switch(arguments.length) { 334 | case 4: 335 | error = a4; 336 | success = a3; 337 | //fallthrough 338 | case 3: 339 | case 2: 340 | if (isFunction(a2)) { 341 | if (isFunction(a1)) { 342 | success = a1; 343 | error = a2; 344 | break; 345 | } 346 | 347 | success = a2; 348 | error = a3; 349 | //fallthrough 350 | } else { 351 | params = a1; 352 | data = a2; 353 | success = a3; 354 | break; 355 | } 356 | case 1: 357 | if (isFunction(a1)) success = a1; 358 | else if (hasBody) data = a1; 359 | else params = a1; 360 | break; 361 | case 0: break; 362 | default: 363 | throw "Expected between 0-4 arguments [params, data, success, error], got " + 364 | arguments.length + " arguments."; 365 | } 366 | 367 | var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data)); 368 | $http({ 369 | method: action.method, 370 | url: route.url(extend({}, extractParams(data), action.params || {}, params)), 371 | data: data 372 | }).then(function(response) { 373 | var data = response.data; 374 | 375 | if (data) { 376 | if (action.isArray) { 377 | value.length = 0; 378 | forEach(data, function(item) { 379 | value.push(new Resource(item)); 380 | }); 381 | } else { 382 | copy(data, value); 383 | } 384 | } 385 | (success||noop)(value, response.headers); 386 | }, error); 387 | 388 | return value; 389 | }; 390 | 391 | 392 | Resource.bind = function(additionalParamDefaults){ 393 | return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); 394 | }; 395 | 396 | 397 | Resource.prototype['$' + name] = function(a1, a2, a3) { 398 | var params = extractParams(this), 399 | success = noop, 400 | error; 401 | 402 | switch(arguments.length) { 403 | case 3: params = a1; success = a2; error = a3; break; 404 | case 2: 405 | case 1: 406 | if (isFunction(a1)) { 407 | success = a1; 408 | error = a2; 409 | } else { 410 | params = a1; 411 | success = a2 || noop; 412 | } 413 | case 0: break; 414 | default: 415 | throw "Expected between 1-3 arguments [params, success, error], got " + 416 | arguments.length + " arguments."; 417 | } 418 | var data = hasBody ? this : undefined; 419 | Resource[name].call(this, params, data, success, error); 420 | }; 421 | }); 422 | return Resource; 423 | } 424 | 425 | return ResourceFactory; 426 | }]); 427 | 428 | })(window, window.angular); 429 | -------------------------------------------------------------------------------- /lib/angular/angular-resource.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.2 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(A,f,u){'use strict';f.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(v,w){function g(b,c){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(c?null:/%20/g,"+")}function l(b,c){this.template=b+="#";this.defaults=c||{};var a=this.urlParams={};j(b.split(/\W/),function(c){c&&b.match(RegExp("[^\\\\]:"+c+"\\W"))&&(a[c]=!0)});this.template=b.replace(/\\:/g,":")}function s(b,c,a){function f(d){var b= 7 | {};j(c||{},function(a,x){var m;a.charAt&&a.charAt(0)=="@"?(m=a.substr(1),m=w(m)(d)):m=a;b[x]=m});return b}function e(a){t(a||{},this)}var y=new l(b),a=r({},z,a);j(a,function(d,g){var l=d.method=="POST"||d.method=="PUT"||d.method=="PATCH";e[g]=function(a,b,c,g){var i={},h,k=o,p=null;switch(arguments.length){case 4:p=g,k=c;case 3:case 2:if(q(b)){if(q(a)){k=a;p=b;break}k=b;p=c}else{i=a;h=b;k=c;break}case 1:q(a)?k=a:l?h=a:i=a;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+ 8 | arguments.length+" arguments.";}var n=this instanceof e?this:d.isArray?[]:new e(h);v({method:d.method,url:y.url(r({},f(h),d.params||{},i)),data:h}).then(function(a){var b=a.data;if(b)d.isArray?(n.length=0,j(b,function(a){n.push(new e(a))})):t(b,n);(k||o)(n,a.headers)},p);return n};e.bind=function(d){return s(b,r({},c,d),a)};e.prototype["$"+g]=function(a,b,d){var c=f(this),i=o,h;switch(arguments.length){case 3:c=a;i=b;h=d;break;case 2:case 1:q(a)?(i=a,h=b):(c=a,i=b||o);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+ 9 | arguments.length+" arguments.";}e[g].call(this,c,l?this:u,i,h)}});return e}var z={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},o=f.noop,j=f.forEach,r=f.extend,t=f.copy,q=f.isFunction;l.prototype={url:function(b){var c=this,a=this.template,f,b=b||{};j(this.urlParams,function(e,d){f=g(b[d]||c.defaults[d]||"",!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+");a=a.replace(RegExp(":"+d+"(\\W)"),f+"$1")});var a= 10 | a.replace(/\/?#$/,""),e=[];j(b,function(a,b){c.urlParams[b]||e.push(g(b)+"="+g(a))});e.sort();a=a.replace(/\/*$/,"");return a+(e.length?"?"+e.join("&"):"")}};return s}])})(window,window.angular); 11 | -------------------------------------------------------------------------------- /lib/angular/angular-sanitize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.2 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular, undefined) { 7 | 'use strict'; 8 | 9 | /** 10 | * @ngdoc overview 11 | * @name ngSanitize 12 | * @description 13 | */ 14 | 15 | /* 16 | * HTML Parser By Misko Hevery (misko@hevery.com) 17 | * based on: HTML Parser By John Resig (ejohn.org) 18 | * Original code by Erik Arvidsson, Mozilla Public License 19 | * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js 20 | * 21 | * // Use like so: 22 | * htmlParser(htmlString, { 23 | * start: function(tag, attrs, unary) {}, 24 | * end: function(tag) {}, 25 | * chars: function(text) {}, 26 | * comment: function(text) {} 27 | * }); 28 | * 29 | */ 30 | 31 | 32 | /** 33 | * @ngdoc service 34 | * @name ngSanitize.$sanitize 35 | * @function 36 | * 37 | * @description 38 | * The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are 39 | * then serialized back to properly escaped html string. This means that no unsafe input can make 40 | * it into the returned string, however, since our parser is more strict than a typical browser 41 | * parser, it's possible that some obscure input, which would be recognized as valid HTML by a 42 | * browser, won't make it through the sanitizer. 43 | * 44 | * @param {string} html Html input. 45 | * @returns {string} Sanitized html. 46 | * 47 | * @example 48 | 49 | 50 | 58 |
59 | Snippet: 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 71 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
FilterSourceRendered
html filter 69 |
<div ng-bind-html="snippet">
</div>
70 |
72 |
73 |
no filter
<div ng-bind="snippet">
</div>
unsafe html filter
<div ng-bind-html-unsafe="snippet">
</div>
86 |
87 |
88 | 89 | it('should sanitize the html snippet ', function() { 90 | expect(using('#html-filter').element('div').html()). 91 | toBe('

an html\nclick here\nsnippet

'); 92 | }); 93 | 94 | it('should escape snippet without any filter', function() { 95 | expect(using('#escaped-html').element('div').html()). 96 | toBe("<p style=\"color:blue\">an html\n" + 97 | "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + 98 | "snippet</p>"); 99 | }); 100 | 101 | it('should inline raw snippet if filtered as unsafe', function() { 102 | expect(using('#html-unsafe-filter').element("div").html()). 103 | toBe("

an html\n" + 104 | "click here\n" + 105 | "snippet

"); 106 | }); 107 | 108 | it('should update', function() { 109 | input('snippet').enter('new text'); 110 | expect(using('#html-filter').binding('snippet')).toBe('new text'); 111 | expect(using('#escaped-html').element('div').html()).toBe("new <b>text</b>"); 112 | expect(using('#html-unsafe-filter').binding("snippet")).toBe('new text'); 113 | }); 114 |
115 |
116 | */ 117 | var $sanitize = function(html) { 118 | var buf = []; 119 | htmlParser(html, htmlSanitizeWriter(buf)); 120 | return buf.join(''); 121 | }; 122 | 123 | 124 | // Regular Expressions for parsing tags and attributes 125 | var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, 126 | END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, 127 | ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, 128 | BEGIN_TAG_REGEXP = /^/g, 131 | CDATA_REGEXP = //g, 132 | URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/, 133 | NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character) 134 | 135 | 136 | // Good source of info about elements and attributes 137 | // http://dev.w3.org/html5/spec/Overview.html#semantics 138 | // http://simon.html5.org/html-elements 139 | 140 | // Safe Void Elements - HTML5 141 | // http://dev.w3.org/html5/spec/Overview.html#void-elements 142 | var voidElements = makeMap("area,br,col,hr,img,wbr"); 143 | 144 | // Elements that you can, intentionally, leave open (and which close themselves) 145 | // http://dev.w3.org/html5/spec/Overview.html#optional-tags 146 | var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), 147 | optionalEndTagInlineElements = makeMap("rp,rt"), 148 | optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements); 149 | 150 | // Safe Block Elements - HTML5 151 | var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," + 152 | "blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," + 153 | "header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")); 154 | 155 | // Inline Elements - HTML5 156 | var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," + 157 | "big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," + 158 | "span,strike,strong,sub,sup,time,tt,u,var")); 159 | 160 | 161 | // Special Elements (can contain anything) 162 | var specialElements = makeMap("script,style"); 163 | 164 | var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements); 165 | 166 | //Attributes that have href and hence need to be sanitized 167 | var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap"); 168 | var validAttrs = angular.extend({}, uriAttrs, makeMap( 169 | 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ 170 | 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ 171 | 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ 172 | 'scope,scrolling,shape,span,start,summary,target,title,type,'+ 173 | 'valign,value,vspace,width')); 174 | 175 | function makeMap(str) { 176 | var obj = {}, items = str.split(','), i; 177 | for (i = 0; i < items.length; i++) obj[items[i]] = true; 178 | return obj; 179 | } 180 | 181 | 182 | /** 183 | * @example 184 | * htmlParser(htmlString, { 185 | * start: function(tag, attrs, unary) {}, 186 | * end: function(tag) {}, 187 | * chars: function(text) {}, 188 | * comment: function(text) {} 189 | * }); 190 | * 191 | * @param {string} html string 192 | * @param {object} handler 193 | */ 194 | function htmlParser( html, handler ) { 195 | var index, chars, match, stack = [], last = html; 196 | stack.last = function() { return stack[ stack.length - 1 ]; }; 197 | 198 | while ( html ) { 199 | chars = true; 200 | 201 | // Make sure we're not in a script or style element 202 | if ( !stack.last() || !specialElements[ stack.last() ] ) { 203 | 204 | // Comment 205 | if ( html.indexOf(""); 207 | 208 | if ( index >= 0 ) { 209 | if (handler.comment) handler.comment( html.substring( 4, index ) ); 210 | html = html.substring( index + 3 ); 211 | chars = false; 212 | } 213 | 214 | // end tag 215 | } else if ( BEGING_END_TAGE_REGEXP.test(html) ) { 216 | match = html.match( END_TAG_REGEXP ); 217 | 218 | if ( match ) { 219 | html = html.substring( match[0].length ); 220 | match[0].replace( END_TAG_REGEXP, parseEndTag ); 221 | chars = false; 222 | } 223 | 224 | // start tag 225 | } else if ( BEGIN_TAG_REGEXP.test(html) ) { 226 | match = html.match( START_TAG_REGEXP ); 227 | 228 | if ( match ) { 229 | html = html.substring( match[0].length ); 230 | match[0].replace( START_TAG_REGEXP, parseStartTag ); 231 | chars = false; 232 | } 233 | } 234 | 235 | if ( chars ) { 236 | index = html.indexOf("<"); 237 | 238 | var text = index < 0 ? html : html.substring( 0, index ); 239 | html = index < 0 ? "" : html.substring( index ); 240 | 241 | if (handler.chars) handler.chars( decodeEntities(text) ); 242 | } 243 | 244 | } else { 245 | html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){ 246 | text = text. 247 | replace(COMMENT_REGEXP, "$1"). 248 | replace(CDATA_REGEXP, "$1"); 249 | 250 | if (handler.chars) handler.chars( decodeEntities(text) ); 251 | 252 | return ""; 253 | }); 254 | 255 | parseEndTag( "", stack.last() ); 256 | } 257 | 258 | if ( html == last ) { 259 | throw "Parse Error: " + html; 260 | } 261 | last = html; 262 | } 263 | 264 | // Clean up any remaining tags 265 | parseEndTag(); 266 | 267 | function parseStartTag( tag, tagName, rest, unary ) { 268 | tagName = angular.lowercase(tagName); 269 | if ( blockElements[ tagName ] ) { 270 | while ( stack.last() && inlineElements[ stack.last() ] ) { 271 | parseEndTag( "", stack.last() ); 272 | } 273 | } 274 | 275 | if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) { 276 | parseEndTag( "", tagName ); 277 | } 278 | 279 | unary = voidElements[ tagName ] || !!unary; 280 | 281 | if ( !unary ) 282 | stack.push( tagName ); 283 | 284 | var attrs = {}; 285 | 286 | rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) { 287 | var value = doubleQuotedValue 288 | || singleQoutedValue 289 | || unqoutedValue 290 | || ''; 291 | 292 | attrs[name] = decodeEntities(value); 293 | }); 294 | if (handler.start) handler.start( tagName, attrs, unary ); 295 | } 296 | 297 | function parseEndTag( tag, tagName ) { 298 | var pos = 0, i; 299 | tagName = angular.lowercase(tagName); 300 | if ( tagName ) 301 | // Find the closest opened tag of the same type 302 | for ( pos = stack.length - 1; pos >= 0; pos-- ) 303 | if ( stack[ pos ] == tagName ) 304 | break; 305 | 306 | if ( pos >= 0 ) { 307 | // Close all the open elements, up the stack 308 | for ( i = stack.length - 1; i >= pos; i-- ) 309 | if (handler.end) handler.end( stack[ i ] ); 310 | 311 | // Remove the open elements from the stack 312 | stack.length = pos; 313 | } 314 | } 315 | } 316 | 317 | /** 318 | * decodes all entities into regular string 319 | * @param value 320 | * @returns {string} A string with decoded entities. 321 | */ 322 | var hiddenPre=document.createElement("pre"); 323 | function decodeEntities(value) { 324 | hiddenPre.innerHTML=value.replace(//g, '>'); 343 | } 344 | 345 | /** 346 | * create an HTML/XML writer which writes to buffer 347 | * @param {Array} buf use buf.jain('') to get out sanitized html string 348 | * @returns {object} in the form of { 349 | * start: function(tag, attrs, unary) {}, 350 | * end: function(tag) {}, 351 | * chars: function(text) {}, 352 | * comment: function(text) {} 353 | * } 354 | */ 355 | function htmlSanitizeWriter(buf){ 356 | var ignore = false; 357 | var out = angular.bind(buf, buf.push); 358 | return { 359 | start: function(tag, attrs, unary){ 360 | tag = angular.lowercase(tag); 361 | if (!ignore && specialElements[tag]) { 362 | ignore = tag; 363 | } 364 | if (!ignore && validElements[tag] == true) { 365 | out('<'); 366 | out(tag); 367 | angular.forEach(attrs, function(value, key){ 368 | var lkey=angular.lowercase(key); 369 | if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) { 370 | out(' '); 371 | out(key); 372 | out('="'); 373 | out(encodeEntities(value)); 374 | out('"'); 375 | } 376 | }); 377 | out(unary ? '/>' : '>'); 378 | } 379 | }, 380 | end: function(tag){ 381 | tag = angular.lowercase(tag); 382 | if (!ignore && validElements[tag] == true) { 383 | out(''); 386 | } 387 | if (tag == ignore) { 388 | ignore = false; 389 | } 390 | }, 391 | chars: function(chars){ 392 | if (!ignore) { 393 | out(encodeEntities(chars)); 394 | } 395 | } 396 | }; 397 | } 398 | 399 | 400 | // define ngSanitize module and register $sanitize service 401 | angular.module('ngSanitize', []).value('$sanitize', $sanitize); 402 | 403 | /** 404 | * @ngdoc directive 405 | * @name ngSanitize.directive:ngBindHtml 406 | * 407 | * @description 408 | * Creates a binding that will sanitize the result of evaluating the `expression` with the 409 | * {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element. 410 | * 411 | * See {@link ngSanitize.$sanitize $sanitize} docs for examples. 412 | * 413 | * @element ANY 414 | * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. 415 | */ 416 | angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) { 417 | return function(scope, element, attr) { 418 | element.addClass('ng-binding').data('$binding', attr.ngBindHtml); 419 | scope.$watch(attr.ngBindHtml, function(value) { 420 | value = $sanitize(value); 421 | element.html(value || ''); 422 | }); 423 | }; 424 | }]); 425 | /** 426 | * @ngdoc filter 427 | * @name ngSanitize.filter:linky 428 | * @function 429 | * 430 | * @description 431 | * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and 432 | * plain email address links. 433 | * 434 | * @param {string} text Input text. 435 | * @returns {string} Html-linkified text. 436 | * 437 | * @usage 438 | 439 | * 440 | * @example 441 | 442 | 443 | 453 |
454 | Snippet: 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 466 | 469 | 470 | 471 | 472 | 473 | 474 | 475 |
FilterSourceRendered
linky filter 464 |
<div ng-bind-html="snippet | linky">
</div>
465 |
467 |
468 |
no filter
<div ng-bind="snippet">
</div>
476 | 477 | 478 | it('should linkify the snippet with urls', function() { 479 | expect(using('#linky-filter').binding('snippet | linky')). 480 | toBe('Pretty text with some links: ' + 481 | 'http://angularjs.org/, ' + 482 | 'us@somewhere.org, ' + 483 | 'another@somewhere.org, ' + 484 | 'and one more: ftp://127.0.0.1/.'); 485 | }); 486 | 487 | it ('should not linkify snippet without the linky filter', function() { 488 | expect(using('#escaped-html').binding('snippet')). 489 | toBe("Pretty text with some links:\n" + 490 | "http://angularjs.org/,\n" + 491 | "mailto:us@somewhere.org,\n" + 492 | "another@somewhere.org,\n" + 493 | "and one more: ftp://127.0.0.1/."); 494 | }); 495 | 496 | it('should update', function() { 497 | input('snippet').enter('new http://link.'); 498 | expect(using('#linky-filter').binding('snippet | linky')). 499 | toBe('new http://link.'); 500 | expect(using('#escaped-html').binding('snippet')).toBe('new http://link.'); 501 | }); 502 | 503 | 504 | */ 505 | angular.module('ngSanitize').filter('linky', function() { 506 | var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/, 507 | MAILTO_REGEXP = /^mailto:/; 508 | 509 | return function(text) { 510 | if (!text) return text; 511 | var match; 512 | var raw = text; 513 | var html = []; 514 | // TODO(vojta): use $sanitize instead 515 | var writer = htmlSanitizeWriter(html); 516 | var url; 517 | var i; 518 | while ((match = raw.match(LINKY_URL_REGEXP))) { 519 | // We can not end in these as they are sometimes found at the end of the sentence 520 | url = match[0]; 521 | // if we did not match ftp/http/mailto then assume mailto 522 | if (match[2] == match[3]) url = 'mailto:' + url; 523 | i = match.index; 524 | writer.chars(raw.substr(0, i)); 525 | writer.start('a', {href:url}); 526 | writer.chars(match[0].replace(MAILTO_REGEXP, '')); 527 | writer.end('a'); 528 | raw = raw.substring(i + match[0].length); 529 | } 530 | writer.chars(raw); 531 | return html.join(''); 532 | }; 533 | }); 534 | 535 | })(window, window.angular); 536 | -------------------------------------------------------------------------------- /lib/angular/angular-sanitize.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.2 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(I,g){'use strict';function i(a){var d={},a=a.split(","),b;for(b=0;b=0;e--)if(f[e]==b)break;if(e>=0){for(c=f.length-1;c>=e;c--)d.end&&d.end(f[c]);f.length= 7 | e}}var c,h,f=[],j=a;for(f.last=function(){return f[f.length-1]};a;){h=!0;if(!f.last()||!q[f.last()]){if(a.indexOf("<\!--")===0)c=a.indexOf("--\>"),c>=0&&(d.comment&&d.comment(a.substring(4,c)),a=a.substring(c+3),h=!1);else if(B.test(a)){if(c=a.match(r))a=a.substring(c[0].length),c[0].replace(r,e),h=!1}else if(C.test(a)&&(c=a.match(s)))a=a.substring(c[0].length),c[0].replace(s,b),h=!1;h&&(c=a.indexOf("<"),h=c<0?a:a.substring(0,c),a=c<0?"":a.substring(c),d.chars&&d.chars(k(h)))}else a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+ 8 | f.last()+"[^>]*>","i"),function(b,a){a=a.replace(D,"$1").replace(E,"$1");d.chars&&d.chars(k(a));return""}),e("",f.last());if(a==j)throw"Parse Error: "+a;j=a}e()}function k(a){l.innerHTML=a.replace(//g,">")}function u(a){var d=!1,b=g.bind(a,a.push);return{start:function(a,c,h){a=g.lowercase(a);!d&&q[a]&&(d=a);!d&&v[a]== 9 | !0&&(b("<"),b(a),g.forEach(c,function(a,c){var e=g.lowercase(c);if(G[e]==!0&&(w[e]!==!0||a.match(H)))b(" "),b(c),b('="'),b(t(a)),b('"')}),b(h?"/>":">"))},end:function(a){a=g.lowercase(a);!d&&v[a]==!0&&(b(""));a==d&&(d=!1)},chars:function(a){d||b(t(a))}}}var s=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,r=/^<\s*\/\s*([\w:-]+)[^>]*>/,A=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,C=/^/g, 10 | E=//g,H=/^((ftp|https?):\/\/|mailto:|#)/,F=/([^\#-~| |!])/g,p=i("area,br,col,hr,img,wbr"),x=i("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),y=i("rp,rt"),o=g.extend({},y,x),m=g.extend({},x,i("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),n=g.extend({},y,i("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")), 11 | q=i("script,style"),v=g.extend({},p,m,n,o),w=i("background,cite,href,longdesc,src,usemap"),G=g.extend({},w,i("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),l=document.createElement("pre");g.module("ngSanitize",[]).value("$sanitize",function(a){var d=[]; 12 | z(a,u(d));return d.join("")});g.module("ngSanitize").directive("ngBindHtml",["$sanitize",function(a){return function(d,b,e){b.addClass("ng-binding").data("$binding",e.ngBindHtml);d.$watch(e.ngBindHtml,function(c){c=a(c);b.html(c||"")})}}]);g.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,d=/^mailto:/;return function(b){if(!b)return b;for(var e=b,c=[],h=u(c),f,g;b=e.match(a);)f=b[0],b[2]==b[3]&&(f="mailto:"+f),g=b.index, 13 | h.chars(e.substr(0,g)),h.start("a",{href:f}),h.chars(b[0].replace(d,"")),h.end("a"),e=e.substring(g+b[0].length);h.chars(e);return c.join("")}})})(window,window.angular); 14 | -------------------------------------------------------------------------------- /lib/angular/jstd-scenario-adapter-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration for jstd scenario adapter 3 | */ 4 | var jstdScenarioAdapter = { 5 | relativeUrlPrefix: '/build/docs/' 6 | }; 7 | -------------------------------------------------------------------------------- /lib/angular/jstd-scenario-adapter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.2 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window) { 7 | 'use strict'; 8 | 9 | /** 10 | * JSTestDriver adapter for angular scenario tests 11 | * 12 | * Example of jsTestDriver.conf for running scenario tests with JSTD: 13 |
 14 |     server: http://localhost:9877
 15 | 
 16 |     load:
 17 |       - lib/angular-scenario.js
 18 |       - lib/jstd-scenario-adapter-config.js
 19 |       - lib/jstd-scenario-adapter.js
 20 |       # your test files go here #
 21 | 
 22 |     proxy:
 23 |      - {matcher: "/your-prefix/*", server: "http://localhost:8000/"}
 24 |   
25 | * 26 | * For more information on how to configure jstd proxy, see {@link http://code.google.com/p/js-test-driver/wiki/Proxy} 27 | * Note the order of files - it's important ! 28 | * 29 | * Example of jstd-scenario-adapter-config.js 30 |
 31 |     var jstdScenarioAdapter = {
 32 |       relativeUrlPrefix: '/your-prefix/'
 33 |     };
 34 |   
35 | * 36 | * Whenever you use browser().navigateTo('relativeUrl') in your scenario test, the relativeUrlPrefix will be prepended. 37 | * You have to configure this to work together with JSTD proxy. 38 | * 39 | * Let's assume you are using the above configuration (jsTestDriver.conf and jstd-scenario-adapter-config.js): 40 | * Now, when you call browser().navigateTo('index.html') in your scenario test, the browser will open /your-prefix/index.html. 41 | * That matches the proxy, so JSTD will proxy this request to http://localhost:8000/index.html. 42 | */ 43 | 44 | /** 45 | * Custom type of test case 46 | * 47 | * @const 48 | * @see jstestdriver.TestCaseInfo 49 | */ 50 | var SCENARIO_TYPE = 'scenario'; 51 | 52 | /** 53 | * Plugin for JSTestDriver 54 | * Connection point between scenario's jstd output and jstestdriver. 55 | * 56 | * @see jstestdriver.PluginRegistrar 57 | */ 58 | function JstdPlugin() { 59 | var nop = function() {}; 60 | 61 | this.reportResult = nop; 62 | this.reportEnd = nop; 63 | this.runScenario = nop; 64 | 65 | this.name = 'Angular Scenario Adapter'; 66 | 67 | /** 68 | * Called for each JSTD TestCase 69 | * 70 | * Handles only SCENARIO_TYPE test cases. There should be only one fake TestCase. 71 | * Runs all scenario tests (under one fake TestCase) and report all results to JSTD. 72 | * 73 | * @param {jstestdriver.TestRunConfiguration} configuration 74 | * @param {Function} onTestDone 75 | * @param {Function} onAllTestsComplete 76 | * @returns {boolean} True if this type of test is handled by this plugin, false otherwise 77 | */ 78 | this.runTestConfiguration = function(configuration, onTestDone, onAllTestsComplete) { 79 | if (configuration.getTestCaseInfo().getType() != SCENARIO_TYPE) return false; 80 | 81 | this.reportResult = onTestDone; 82 | this.reportEnd = onAllTestsComplete; 83 | this.runScenario(); 84 | 85 | return true; 86 | }; 87 | 88 | this.getTestRunsConfigurationFor = function(testCaseInfos, expressions, testRunsConfiguration) { 89 | testRunsConfiguration.push( 90 | new jstestdriver.TestRunConfiguration( 91 | new jstestdriver.TestCaseInfo( 92 | 'Angular Scenario Tests', function() {}, SCENARIO_TYPE), [])); 93 | 94 | return true; 95 | }; 96 | } 97 | 98 | /** 99 | * Singleton instance of the plugin 100 | * Accessed using closure by: 101 | * - jstd output (reports to this plugin) 102 | * - initScenarioAdapter (register the plugin to jstd) 103 | */ 104 | var plugin = new JstdPlugin(); 105 | 106 | /** 107 | * Initialise scenario jstd-adapter 108 | * (only if jstestdriver is defined) 109 | * 110 | * @param {Object} jstestdriver Undefined when run from browser (without jstd) 111 | * @param {Function} initScenarioAndRun Function that inits scenario and runs all the tests 112 | * @param {Object=} config Configuration object, supported properties: 113 | * - relativeUrlPrefix: prefix for all relative links when navigateTo() 114 | */ 115 | function initScenarioAdapter(jstestdriver, initScenarioAndRun, config) { 116 | if (jstestdriver) { 117 | // create and register ScenarioPlugin 118 | jstestdriver.pluginRegistrar.register(plugin); 119 | plugin.runScenario = initScenarioAndRun; 120 | 121 | /** 122 | * HACK (angular.scenario.Application.navigateTo) 123 | * 124 | * We need to navigate to relative urls when running from browser (without JSTD), 125 | * because we want to allow running scenario tests without creating its own virtual host. 126 | * For example: http://angular.local/build/docs/docs-scenario.html 127 | * 128 | * On the other hand, when running with JSTD, we need to navigate to absolute urls, 129 | * because of JSTD proxy. (proxy, because of same domain policy) 130 | * 131 | * So this hack is applied only if running with JSTD and change all relative urls to absolute. 132 | */ 133 | var appProto = angular.scenario.Application.prototype, 134 | navigateTo = appProto.navigateTo, 135 | relativeUrlPrefix = config && config.relativeUrlPrefix || '/'; 136 | 137 | appProto.navigateTo = function(url, loadFn, errorFn) { 138 | if (url.charAt(0) != '/' && url.charAt(0) != '#' && 139 | url != 'about:blank' && !url.match(/^https?/)) { 140 | url = relativeUrlPrefix + url; 141 | } 142 | 143 | return navigateTo.call(this, url, loadFn, errorFn); 144 | }; 145 | } 146 | } 147 | 148 | /** 149 | * Builds proper TestResult object from given model spec 150 | * 151 | * TODO(vojta) report error details 152 | * 153 | * @param {angular.scenario.ObjectModel.Spec} spec 154 | * @returns {jstestdriver.TestResult} 155 | */ 156 | function createTestResultFromSpec(spec) { 157 | var map = { 158 | success: 'PASSED', 159 | error: 'ERROR', 160 | failure: 'FAILED' 161 | }; 162 | 163 | return new jstestdriver.TestResult( 164 | spec.fullDefinitionName, 165 | spec.name, 166 | jstestdriver.TestResult.RESULT[map[spec.status]], 167 | spec.error || '', 168 | spec.line || '', 169 | spec.duration); 170 | } 171 | 172 | /** 173 | * Generates JSTD output (jstestdriver.TestResult) 174 | */ 175 | angular.scenario.output('jstd', function(context, runner, model) { 176 | model.on('SpecEnd', function(spec) { 177 | plugin.reportResult(createTestResultFromSpec(spec)); 178 | }); 179 | 180 | model.on('RunnerEnd', function() { 181 | plugin.reportEnd(); 182 | }); 183 | }); 184 | initScenarioAdapter(window.jstestdriver, angular.scenario.setUpAndRun, window.jstdScenarioAdapter); 185 | })(window); 186 | -------------------------------------------------------------------------------- /lib/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function(e){e(function(){"use strict";e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()},e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e(function(){e("body").on("click.alert.data-api",t,n.prototype.close)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")},e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e(function(){e("body").on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=n,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},to:function(t){var n=this.$element.find(".item.active"),r=n.parent().children(),i=r.index(n),s=this;if(t>r.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){s.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",e(r[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f=e.Event("slide",{relatedTarget:i[0]});this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u]();if(i.hasClass("active"))return;if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}},e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e(function(){e("body").on("click.carousel.data-api","[data-slide]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=!i.data("modal")&&e.extend({},i.data(),n.data());i.carousel(s),t.preventDefault()})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning)return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning)return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=typeof n=="object"&&n;i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e(function(){e("body").on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})})}(window.jQuery),!function(e){"use strict";function r(){i(e(t)).removeClass("open")}function i(t){var n=t.attr("data-target"),r;return n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=e(n),r.length||(r=t.parent()),r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||(s.toggleClass("open"),n.focus()),!1},keydown:function(t){var n,r,s,o,u,a;if(!/(38|40|27)/.test(t.keyCode))return;n=e(this),t.preventDefault(),t.stopPropagation();if(n.is(".disabled, :disabled"))return;o=i(n),u=o.hasClass("open");if(!u||u&&t.keyCode==27)return n.click();r=e("[role=menu] li:not(.divider) a",o);if(!r.length)return;a=r.index(r.filter(":focus")),t.keyCode==38&&a>0&&a--,t.keyCode==40&&a').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,e.proxy(this.removeBackdrop,this)):this.removeBackdrop()):t&&t()}},e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e(function(){e("body").on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,this.options.trigger=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):this.options.trigger!="manual"&&(i=this.options.trigger=="hover"?"mouseenter":"focus",s=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this))),this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,t,this.$element.data()),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var e,t,n,r,i,s,o;if(this.hasContent()&&this.enabled){e=this.tip(),this.setContent(),this.options.animation&&e.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,t=/in/.test(s),e.remove().css({top:0,left:0,display:"block"}).appendTo(t?this.$element:document.body),n=this.getPosition(t),r=e[0].offsetWidth,i=e[0].offsetHeight;switch(t?s.split(" ")[1]:s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}e.css(o).addClass(s).addClass("in")}},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function r(){var t=setTimeout(function(){n.off(e.support.transition.end).remove()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.remove()})}var t=this,n=this.tip();return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?r():n.remove(),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(t){return e.extend({},t?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}},e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'
',trigger:"hover",title:"",delay:0,html:!0}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content > *")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-content")||(typeof n.content=="function"?n.content.call(t[0]):n.content),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}}),e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'

'})}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var t=e(this),n=t.data("target")||t.attr("href"),r=/^#\w/.test(n)&&e(n);return r&&r.length&&[[r.position().top,n]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}},e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active a").last()[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}},e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e(function(){e("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=e(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:t.top+t.height,left:t.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),(e.browser.chrome||e.browser.webkit||e.browser.msie)&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this))},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=!~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},blur:function(e){var t=this;setTimeout(function(){t.hide()},150)},click:function(e){e.stopPropagation(),e.preventDefault(),this.select()},mouseenter:function(t){this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")}},e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1},e.fn.typeahead.Constructor=t,e(function(){e("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;t.preventDefault(),n.typeahead(n.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))},e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery); -------------------------------------------------------------------------------- /lib/codemirror.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | line-height: 1em; 3 | font-family: monospace; 4 | 5 | /* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */ 6 | position: relative; 7 | /* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */ 8 | overflow: hidden; 9 | } 10 | 11 | .CodeMirror-scroll { 12 | overflow: auto; 13 | height: 300px; 14 | /* This is needed to prevent an IE[67] bug where the scrolled content 15 | is visible outside of the scrolling box. */ 16 | position: relative; 17 | outline: none; 18 | } 19 | 20 | /* Vertical scrollbar */ 21 | .CodeMirror-scrollbar { 22 | position: absolute; 23 | right: 0; top: 0; 24 | overflow-x: hidden; 25 | overflow-y: scroll; 26 | z-index: 5; 27 | } 28 | .CodeMirror-scrollbar-inner { 29 | /* This needs to have a nonzero width in order for the scrollbar to appear 30 | in Firefox and IE9. */ 31 | width: 1px; 32 | } 33 | .CodeMirror-scrollbar.cm-sb-overlap { 34 | /* Ensure that the scrollbar appears in Lion, and that it overlaps the content 35 | rather than sitting to the right of it. */ 36 | position: absolute; 37 | z-index: 1; 38 | float: none; 39 | right: 0; 40 | min-width: 12px; 41 | } 42 | .CodeMirror-scrollbar.cm-sb-nonoverlap { 43 | min-width: 12px; 44 | } 45 | .CodeMirror-scrollbar.cm-sb-ie7 { 46 | min-width: 18px; 47 | } 48 | 49 | .CodeMirror-gutter { 50 | position: absolute; left: 0; top: 0; 51 | z-index: 10; 52 | background-color: #f7f7f7; 53 | border-right: 1px solid #eee; 54 | min-width: 2em; 55 | height: 100%; 56 | } 57 | .CodeMirror-gutter-text { 58 | color: #aaa; 59 | text-align: right; 60 | padding: .4em .2em .4em .4em; 61 | white-space: pre !important; 62 | cursor: default; 63 | } 64 | .CodeMirror-lines { 65 | padding: .4em; 66 | white-space: pre; 67 | cursor: text; 68 | } 69 | 70 | .CodeMirror pre { 71 | -moz-border-radius: 0; 72 | -webkit-border-radius: 0; 73 | -o-border-radius: 0; 74 | border-radius: 0; 75 | border-width: 0; margin: 0; padding: 0; background: transparent; 76 | font-family: inherit; 77 | font-size: inherit; 78 | padding: 0; margin: 0; 79 | white-space: pre; 80 | word-wrap: normal; 81 | line-height: inherit; 82 | color: inherit; 83 | overflow: visible; 84 | } 85 | 86 | .CodeMirror-wrap pre { 87 | word-wrap: break-word; 88 | white-space: pre-wrap; 89 | word-break: normal; 90 | } 91 | .CodeMirror-wrap .CodeMirror-scroll { 92 | overflow-x: hidden; 93 | } 94 | 95 | .CodeMirror textarea { 96 | outline: none !important; 97 | } 98 | 99 | .CodeMirror pre.CodeMirror-cursor { 100 | z-index: 10; 101 | position: absolute; 102 | visibility: hidden; 103 | border-left: 1px solid black; 104 | border-right: none; 105 | width: 0; 106 | } 107 | .cm-keymap-fat-cursor pre.CodeMirror-cursor { 108 | width: auto; 109 | border: 0; 110 | background: transparent; 111 | background: rgba(0, 200, 0, .4); 112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); 113 | } 114 | /* Kludge to turn off filter in ie9+, which also accepts rgba */ 115 | .cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) { 116 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 117 | } 118 | .CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} 119 | .CodeMirror-focused pre.CodeMirror-cursor { 120 | visibility: visible; 121 | } 122 | 123 | div.CodeMirror-selected { background: #d9d9d9; } 124 | .CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; } 125 | 126 | .CodeMirror-searching { 127 | background: #ffa; 128 | background: rgba(255, 255, 0, .4); 129 | } 130 | 131 | /* Default theme */ 132 | 133 | .cm-s-default span.cm-keyword {color: #708;} 134 | .cm-s-default span.cm-atom {color: #219;} 135 | .cm-s-default span.cm-number {color: #164;} 136 | .cm-s-default span.cm-def {color: #00f;} 137 | .cm-s-default span.cm-variable {color: black;} 138 | .cm-s-default span.cm-variable-2 {color: #05a;} 139 | .cm-s-default span.cm-variable-3 {color: #085;} 140 | .cm-s-default span.cm-property {color: black;} 141 | .cm-s-default span.cm-operator {color: black;} 142 | .cm-s-default span.cm-comment {color: #a50;} 143 | .cm-s-default span.cm-string {color: #a11;} 144 | .cm-s-default span.cm-string-2 {color: #f50;} 145 | .cm-s-default span.cm-meta {color: #555;} 146 | .cm-s-default span.cm-error {color: #f00;} 147 | .cm-s-default span.cm-qualifier {color: #555;} 148 | .cm-s-default span.cm-builtin {color: #30a;} 149 | .cm-s-default span.cm-bracket {color: #997;} 150 | .cm-s-default span.cm-tag {color: #170;} 151 | .cm-s-default span.cm-attribute {color: #00c;} 152 | .cm-s-default span.cm-header {color: blue;} 153 | .cm-s-default span.cm-quote {color: #090;} 154 | .cm-s-default span.cm-hr {color: #999;} 155 | .cm-s-default span.cm-link {color: #00c;} 156 | 157 | span.cm-header, span.cm-strong {font-weight: bold;} 158 | span.cm-em {font-style: italic;} 159 | span.cm-emstrong {font-style: italic; font-weight: bold;} 160 | span.cm-link {text-decoration: underline;} 161 | 162 | span.cm-invalidchar {color: #f00;} 163 | 164 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 165 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 166 | 167 | @media print { 168 | 169 | /* Hide the cursor when printing */ 170 | .CodeMirror pre.CodeMirror-cursor { 171 | visibility: hidden; 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /lib/mode_gcode/gcode_mode.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("diff", function() { 2 | 3 | var TOKEN_NAMES = { 4 | 'G': 'tag', 5 | 'M': 'string', 6 | ';': 'meta' 7 | }; 8 | 9 | return { 10 | token: function(stream) { 11 | var tw_pos = stream.string.search(/[\t ]+?$/); 12 | 13 | if (!stream.sol() || tw_pos === 0) { 14 | stream.skipToEnd(); 15 | return ("error " + ( 16 | TOKEN_NAMES[stream.string.charAt(0)] || '')).replace(/ $/, ''); 17 | } 18 | 19 | var token_name = TOKEN_NAMES[stream.peek()] || stream.skipToEnd(); 20 | 21 | if (tw_pos === -1) { 22 | stream.skipToEnd(); 23 | } else { 24 | stream.pos = tw_pos; 25 | } 26 | 27 | return token_name; 28 | } 29 | }; 30 | }); 31 | 32 | CodeMirror.defineMIME("text/x-diff", "diff"); 33 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: * 3 | Sitemap: /sitemap.xml -------------------------------------------------------------------------------- /sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | http://gcode.ws/index.html 4 | --------------------------------------------------------------------------------