├── .babelrc ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .travis ├── after_success.sh └── deploy-key.enc ├── CNAME ├── README.md ├── UI resources └── hack-folder-app-icon.sketch │ ├── Data │ ├── metadata │ └── version ├── assets ├── csv.js ├── handlebars-v1.3.0.js ├── img │ ├── app-icon │ │ ├── icon-60.png │ │ ├── icon-60@2x.png │ │ ├── icon-76.png │ │ └── icon-76@2x.png │ └── favicon │ │ └── favicon.ico ├── jquery-ui │ └── 1.11.0.custom │ │ ├── external │ │ └── jquery │ │ │ └── jquery.js │ │ ├── index.html │ │ ├── jquery-ui.css │ │ ├── jquery-ui.js │ │ ├── jquery-ui.min.css │ │ ├── jquery-ui.min.js │ │ ├── jquery-ui.structure.css │ │ └── jquery-ui.structure.min.css ├── jquery │ ├── 1.10.2 │ │ ├── jquery-1.10.2.min.map │ │ ├── jquery.js │ │ └── jquery.min.js │ └── jquery-2.1.1.min.js ├── semantic-ui │ └── 0.19.0 │ │ ├── css │ │ ├── semantic.css │ │ └── semantic.min.css │ │ ├── fonts │ │ ├── basic.icons.eot │ │ ├── basic.icons.svg │ │ ├── basic.icons.ttf │ │ ├── basic.icons.woff │ │ ├── icons.eot │ │ ├── icons.otf │ │ ├── icons.svg │ │ ├── icons.ttf │ │ └── icons.woff │ │ ├── images │ │ ├── loader-large-inverted.gif │ │ ├── loader-large.gif │ │ ├── loader-medium-inverted.gif │ │ ├── loader-medium.gif │ │ ├── loader-mini-inverted.gif │ │ ├── loader-mini.gif │ │ ├── loader-small-inverted.gif │ │ └── loader-small.gif │ │ └── javascript │ │ ├── semantic.js │ │ └── semantic.min.js └── tabletop │ └── tabletop.js ├── deploy ├── deploy.bat ├── deploy.sh ├── docs ├── Developing Hackfoldr 2.0 zh-tw.md ├── Hosting your own Hackfoldr 2.0 zh-tw.md ├── Hosting your own Hackfoldr 2.0.md └── imgs │ └── gh-pages-branch.png ├── gulpfile.babel.js ├── js └── index.js ├── package.json ├── sass └── global.sass └── views ├── 404.jade ├── _template.jade ├── foldr_history-menu.jade ├── help-menu.jade ├── index.jade ├── shortcuts-menu.jade ├── white_label-shortcuts-dropdown.jade ├── white_label-shortcuts-nav.jade ├── white_label-shortcuts-topbar.jade └── zoom-menu.jade /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | 24 | # Windows batch files should always have CRLF line endings 25 | *.bat text eol=crlf 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | 217 | ############# 218 | ## Fire.app 219 | ############# 220 | 221 | .sass-cache 222 | .livescript-cache 223 | fire_app_log.txt 224 | .editorconfig 225 | public/library.html 226 | 227 | ############# 228 | ## Node.js 229 | ############# 230 | 231 | node_modules 232 | _public 233 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '0.10' 5 | 6 | git: 7 | depth: 1 8 | 9 | before_install: 10 | - gem update --system 11 | - gem install compass --no-document 12 | - gem install tilt --version "1.4.1" --no-document 13 | 14 | before_script: 15 | - chmod +x .travis/after_success.sh 16 | - chmod +x ./deploy 17 | 18 | script: 19 | - npm test 20 | 21 | branches: 22 | except: 23 | - gh-pages 24 | 25 | env: 26 | global: 27 | - REPO="" 28 | - secure: "" 29 | 30 | after_success: .travis/after_success.sh 31 | -------------------------------------------------------------------------------- /.travis/after_success.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [ "$TRAVIS_PULL_REQUEST" != "false" ] || [ "$TRAVIS_BRANCH" != "master" ] && echo -e "\n" && exit 0 3 | 4 | git remote set-url origin $REPO.git 5 | git config --global user.email "g0v-general@googlegroups.com" 6 | git config --global user.name "g0v general (via TravisCI)" 7 | mkdir -p ~/.ssh 8 | cat >> ~/.ssh/config <>> Current Repo:$REPO --- Travis Branch:$TRAVIS_BRANCH" 29 | ./deploy -------------------------------------------------------------------------------- /.travis/deploy-key.enc: -------------------------------------------------------------------------------- 1 | U2FsdGVkX181cMBhGmeyDlrdPgTEzSJtr1QBFeUzrGAwTKTLEtnYW6RYhC6ACJL5 2 | IxBlPzIH8JruiTO03qx8bpnFddNf8JHuDTDqeJL/+5HWilH2jSE4ICKWQGaBX124 3 | /YQnacElxb/9zR1IpYk2bMSP/twrFcfc5yYe/kB0ErA+Vx4zX5mHhgb1GxAjxrZ7 4 | rtf8uKr26M9DujiFy3Ljpkq4Trr0LwG6/c1v7dwtFGLNeMVJVUaOTD5NmSCeIDMK 5 | Nn1ooHJ6nmgwhVqzAcl8m+vqs1JS53lLHrCr7jO6HSaHxE3FLd/kdJDJyL15g/0O 6 | B7JOQEJ9Th0Ju/W9ICAEGgG5mS83wxAd7/wNs42nPErzkHsrsiqYOoxLplm2o93s 7 | XEBHUKLBfG8ZRoLwI5I7/yFqN4TxjSXyrtP9H6mprRNqJt36zPEK3PYSEjWs5E6b 8 | EAqwT0XdHSEOg9+4M8azR859hLN0sGLBE01B81RtKI3zRd8oM+RCkuRe1PkYGuPl 9 | cTeUZqeBOVRaXqT0iBwRfLjdC3dWUgaSKQY3Qz8JXYwMH00kXC9qUeVupv2PfztW 10 | dG7WiP+yrhio7rfSF21OACpXLUUum7xzNTrh7gS1kvkR6WPYTFOtHlblOh12fivE 11 | zbq13OxT62a7KIy71cqksMIycZEUtUornuX+z9VkM703TMA11nNsZVcrRbgjCSRa 12 | h4ycwCSsRITzKHZ1ZDMYwrPtmVEw3xjCaYKFwSv+KYDFD0l9ChnGUgvJVGMngsUG 13 | MsXmNgnixQMgyYqP0w/N0qgy+ZqL2lFdJNnIi4hPyU/RdkXa/Lr3YOnvip0A7nLx 14 | qBKNZV+a3G448fJF7hL4/l5Bi4mEDkWW/WynUQa2FX+rqX1O9XrHM2N8CSYyDun+ 15 | NEDpULPX43T6QqDg1Xv1/CsC+H3pcoysPY67EDij2F/2yxAM/FJZsAPCygxcAtM2 16 | RCFNLo0ysy+YZSXteHO4+Lot9qGcoS1EycKxDQCbzN/Ar4vlLpCKKcGAD3fzuXD/ 17 | AsSFMfQyTlD5O31BT4E3K+cXtRFXqigP2fznzfK3chAqZJVkXpUldXuLqqhrYjJ9 18 | QD+j3z2ZIc5w39/14D8JZp/pO//ta8B8fsVuIA9XQTGVdTXKiEnbf05RAPJ4ubEn 19 | G+2YkEwGLqk/GqcnId8ftGwCrcTpkX01bszhxiAq5H8CAUnvG34bmt85KAAGyEP0 20 | kZ1bODx9Zikv7sfivv4ZIth4v79bwJ7Bwy0YWyy+2ObQM61UZXKvOSXenQJFErjh 21 | V2j/ES4t1ANi8Qn1/Ylu6R/E4IheeQ79vBc6+8KZCQVhBNhjJqKWb+UWr+iEkwrE 22 | +TaEFVlfq5O/ypQtinlk9htHFG8Q4mjRvd5fn+IceqSf/XrZlyRQH/u9FpAFY4M1 23 | CAPr3wGOYlKpMwphTyoAcxTQUVrVY7k17ioxDMwJ7gGJUEdwH7NtSpiArQQXSc40 24 | r6TjyVfhQmzgJBhe8cLntOFuqEtHIoWNtReZjOHzrZHTui+uovtxlC7kJXiNcL92 25 | IqGDdtU6W2XHzeU8cyFkeq3rFdAhprU+Vp1nz5MLSsRG+Te7gZ3woQCWWSp1f0TZ 26 | a6LoUw4L+lQQkscgEnlmZSwQcLnsk/MhZawa0IeyLP0n/56gbpSdo+jzGbhyk7dP 27 | xsZCxiuLW1pnyyAiAD0nlyPVT7198sQ8re6kp7Evjiqrxr1pxWFckCpVqDkadPHB 28 | Stm83xbndF/ZGxKk28Aee1L0cDZvb6+5ywSLUn91u2d7uH5Rpu3aedYYwFzG54Sy 29 | l6Ic8e3EOnXpjZsHYKKrc97y7RYMIfwH5Y4bFmFKaPl2Gs76j8wLdAEQkhnpasad 30 | FR8jqZtA45BZarrHiWiykdPc9Ab35+8b4h1OOBM++kDo7CVbEc7+Vur/fHo8RCb9 31 | 3dtHOKmTtxp7h9adoxD5oz+PH0lhjMS5PsWcSNamxmPbLgPS+DEp44WObXjHQBGo 32 | k+1fkTrTp/Mymjomd4RvSuiDa7/J3DJqMpwZSXA1T24kyDRvX9LI6BIa+1fjd2AS 33 | qrt+aeLZLIdrZVJu6X+rxaso8Bs61R5ZkyHozjZMSPD4Ns+eBHz5IKAT5Nq7F8mF 34 | OuH7/kgo8UkzRpEYHOLz2577S+4SM+vc8pmVXNvw3WmtYAOObQrzXYajkluJqrW0 35 | +bQfA/hlFWxYxGe1atK/LIjyLVhlPMWxC3sZ8ZfkRSG6ukRs9Ke3VLCBjOYIuyc0 36 | qDO1EFZ/DT+5Mn6gtJGIug== 37 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | (your domain name) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The New Hackfoldr 2 | ============ 3 | 4 | Organize gdoc and hackpad documents for project. 5 | 6 | ### Why? 7 | 8 | We need a way to organize many dynamic documents for every projects. 9 | 10 | The shared folder feature in Google Docs comes very close to what we want, but as every document is opened in edit mode, it soon becomes unusable. It is also impossible to sort the items and we had to use numeric prefix to achieve that. 11 | 12 | Hackpad collections are great too, but we also want to include spreadsheets as one of the item types. 13 | 14 | So we build this small single-page static web application, that reads a list of url from an EtherCalc spreadsheet, rendering it in a way similar to a google docs folder. If the document supports read-only mode, we use that by default when it is opened by the user, and provide an additional edit link. 15 | 16 | 17 | ### Supported document types 18 | 19 | * Google Docs 20 | * Google Spreadsheets 21 | * Google Prensetation 22 | * Google Drawing 23 | * Hackpad 24 | * EtherCalc 25 | * Links 26 | 27 | 28 | ## Sample folder 29 | 30 | http://folder.moztw.org/hackfolder_template 31 | 32 | 33 | # Hosting your own Hackfoldr 34 | [Please follow the toturial](https://github.com/hackfoldr/hackfoldr-2.0-forkme/blob/master/docs/Hosting%20your%20own%20Hackfoldr%202.0.md) to set up your own hackfoldr 2.0 instance 35 | 36 | 37 | # Development 38 | 39 | [![devDependency Status](https://david-dm.org/hackfoldr/hackfoldr-2.0-forkme/dev-status.svg?style=flat-square)](https://david-dm.org/hackfoldr/hackfoldr-2.0-forkme#info=devDependencies) 40 | 41 | * development on master branch 42 | * deploy on gh-pages branch (It would auto deploy via travis-ci. You only commit on master branch and push.) 43 | ^^^ temporary broken ^^^ 44 | 45 | 46 | ### Developing with Gulp.js 47 | 48 | * pre-dev: 49 | * install: [node](http://nodejs.org/) 50 | * `npm i` 51 | * devlopment: 52 | * `npm start` 53 | * open `http://localhost:3000/` to see the result. You can use `http://localhost:3000/hackfolder_template` to development. 54 | 55 | 56 | # License 57 | 58 | ## CC0 1.0 Universal 59 | 60 | http://creativecommons.org/publicdomain/zero/1.0 61 | 62 | To the extent possible under law, the original author [Chia-liang Kao](https://github.com/clkao) has waived all copyright and related or neighboring rights to hackfoldr. 63 | 64 | Thanks to all contributors for [Hackfoldr](https://github.com/hackfoldr/hackfoldr/graphs/contributors) and [Hackfoldr 2.0](https://github.com/hackfoldr/hackfoldr-2.0-forkme/graphs/contributors) 65 | 66 | This work is published from Taiwan. 67 | -------------------------------------------------------------------------------- /UI resources/hack-folder-app-icon.sketch/Data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/UI resources/hack-folder-app-icon.sketch/Data -------------------------------------------------------------------------------- /UI resources/hack-folder-app-icon.sketch/metadata: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | app 6 | com.bohemiancoding.sketch3 7 | build 8 | 8054 9 | commit 10 | b2079fe10151ad0eef6cc553b7369ec78d67b9b5 11 | fonts 12 | 13 | length 14 | 468785 15 | version 16 | 37 17 | 18 | 19 | -------------------------------------------------------------------------------- /UI resources/hack-folder-app-icon.sketch/version: -------------------------------------------------------------------------------- 1 | 37 -------------------------------------------------------------------------------- /assets/csv.js: -------------------------------------------------------------------------------- 1 | /* 2 | CSV-JS - A Comma-Separated Values parser for JS 3 | 4 | Built to rfc4180 standard, with options for adjusting strictness: 5 | - optional carriage returns for non-microsoft sources 6 | - automatically type-cast numeric an boolean values 7 | - relaxed mode which: ignores blank lines, ignores gargabe following quoted tokens, does not enforce a consistent record length 8 | 9 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | Author Greg Kindel (twitter @gkindel), 2013 22 | */ 23 | 24 | (function () { 25 | /** 26 | * @name CSV 27 | * @namespace 28 | */ 29 | // implemented as a singleton because JS is single threaded 30 | var CSV = {}; 31 | CSV.RELAXED = false; 32 | CSV.IGNORE_RECORD_LENGTH = false; 33 | CSV.IGNORE_QUOTES = false; 34 | CSV.LINE_FEED_OK = true; 35 | CSV.CARRIAGE_RETURN_OK = true; 36 | CSV.DETECT_TYPES = true; 37 | CSV.IGNORE_QUOTE_WHITESPACE = true; 38 | CSV.DEBUG = false; 39 | 40 | CSV.ERROR_EOF = "UNEXPECTED_END_OF_FILE"; 41 | CSV.ERROR_CHAR = "UNEXPECTED_CHARACTER"; 42 | CSV.ERROR_EOL = "UNEXPECTED_END_OF_RECORD"; 43 | CSV.WARN_SPACE = "UNEXPECTED_WHITESPACE"; // not per spec, but helps debugging 44 | 45 | var QUOTE = "\"", 46 | CR = "\r", 47 | LF = "\n", 48 | COMMA = ",", 49 | SPACE = " ", 50 | TAB = "\t"; 51 | 52 | // states 53 | var PRE_TOKEN = 0, 54 | MID_TOKEN = 1, 55 | POST_TOKEN = 2, 56 | POST_RECORD = 4; 57 | /** 58 | * @name CSV.parse 59 | * @function 60 | * @description rfc4180 standard csv parse 61 | * with options for strictness and data type conversion 62 | * By default, will automatically type-cast numeric an boolean values. 63 | * @param {String} str A CSV string 64 | * @return {Array} An array records, each of which is an array of scalar values. 65 | * @example 66 | * // simple 67 | * var rows = CSV.parse("one,two,three\nfour,five,six") 68 | * // rows equals [["one","two","three"],["four","five","six"]] 69 | * @example 70 | * // Though not a jQuery plugin, it is recommended to use with the $.ajax pipe() method: 71 | * $.get("csv.txt") 72 | * .pipe( CSV.parse ) 73 | * .done( function(rows) { 74 | * for( var i =0; i < rows.length; i++){ 75 | * console.log(rows[i]) 76 | * } 77 | * }); 78 | * @see http://www.ietf.org/rfc/rfc4180.txt 79 | */ 80 | CSV.parse = function (str) { 81 | var result = CSV.result = []; 82 | CSV.offset = 0; 83 | CSV.str = str; 84 | CSV.record_begin(); 85 | 86 | CSV.debug("parse()", str); 87 | 88 | var c; 89 | while( 1 ){ 90 | // pull char 91 | c = str[CSV.offset++]; 92 | CSV.debug("c", c); 93 | 94 | // detect eof 95 | if (c == null) { 96 | if( CSV.escaped ) 97 | CSV.error(CSV.ERROR_EOF); 98 | 99 | if( CSV.record ){ 100 | CSV.token_end(); 101 | CSV.record_end(); 102 | } 103 | 104 | CSV.debug("...bail", c, CSV.state, CSV.record); 105 | CSV.reset(); 106 | break; 107 | } 108 | 109 | if( CSV.record == null ){ 110 | // if relaxed mode, ignore blank lines 111 | if( CSV.RELAXED && (c == LF || c == CR && str[CSV.offset + 1] == LF) ){ 112 | continue; 113 | } 114 | CSV.record_begin(); 115 | } 116 | 117 | // pre-token: look for start of escape sequence 118 | if (CSV.state == PRE_TOKEN) { 119 | 120 | if ( (c === SPACE || c === TAB) && CSV.next_nonspace() == QUOTE ){ 121 | if( CSV.RELAXED || CSV.IGNORE_QUOTE_WHITESPACE ) { 122 | continue; 123 | } 124 | else { 125 | // not technically an error, but ambiguous and hard to debug otherwise 126 | CSV.warn(CSV.WARN_SPACE); 127 | } 128 | } 129 | 130 | if (c == QUOTE && ! CSV.IGNORE_QUOTES) { 131 | CSV.debug("...escaped start", c); 132 | CSV.escaped = true; 133 | CSV.state = MID_TOKEN; 134 | continue; 135 | } 136 | CSV.state = MID_TOKEN; 137 | } 138 | 139 | // mid-token and escaped, look for sequences and end quote 140 | if (CSV.state == MID_TOKEN && CSV.escaped) { 141 | if (c == QUOTE) { 142 | if (str[CSV.offset] == QUOTE) { 143 | CSV.debug("...escaped quote", c); 144 | CSV.token += QUOTE; 145 | CSV.offset++; 146 | } 147 | else { 148 | CSV.debug("...escaped end", c); 149 | CSV.escaped = false; 150 | CSV.state = POST_TOKEN; 151 | } 152 | } 153 | else { 154 | CSV.token += c; 155 | CSV.debug("...escaped add", c, CSV.token); 156 | } 157 | continue; 158 | } 159 | 160 | // fall-through: mid-token or post-token, not escaped 161 | if (c == CR ) { 162 | if( str[CSV.offset] == LF ) 163 | CSV.offset++; 164 | else if( ! CSV.CARRIAGE_RETURN_OK ) 165 | CSV.error(CSV.ERROR_CHAR); 166 | CSV.token_end(); 167 | CSV.record_end(); 168 | } 169 | else if (c == LF) { 170 | if( ! (CSV.LINE_FEED_OK || CSV.RELAXED) ) 171 | CSV.error(CSV.ERROR_CHAR); 172 | CSV.token_end(); 173 | CSV.record_end(); 174 | } 175 | else if (c == COMMA) { 176 | CSV.token_end(); 177 | } 178 | else if( CSV.state == MID_TOKEN ){ 179 | CSV.token += c; 180 | CSV.debug("...add", c, CSV.token); 181 | } 182 | else if ( c === SPACE || c === TAB) { 183 | if (! CSV.IGNORE_QUOTE_WHITESPACE ) 184 | CSV.error(CSV.WARN_SPACE ); 185 | } 186 | else if( ! CSV.RELAXED ){ 187 | CSV.error(CSV.ERROR_CHAR); 188 | } 189 | } 190 | return result; 191 | }; 192 | 193 | CSV.reset = function () { 194 | CSV.state = null; 195 | CSV.token = null; 196 | CSV.escaped = null; 197 | CSV.record = null; 198 | CSV.offset = null; 199 | CSV.result = null; 200 | CSV.str = null; 201 | }; 202 | 203 | CSV.next_nonspace = function () { 204 | var i = CSV.offset; 205 | var c; 206 | while( i < CSV.str.length ) { 207 | c = CSV.str[i++]; 208 | if( !( c == SPACE || c === TAB ) ){ 209 | return c; 210 | } 211 | } 212 | return null; 213 | }; 214 | 215 | CSV.record_begin = function () { 216 | CSV.escaped = false; 217 | CSV.record = []; 218 | CSV.token_begin(); 219 | CSV.debug("record_begin"); 220 | }; 221 | 222 | CSV.record_end = function () { 223 | CSV.state = POST_RECORD; 224 | if( ! (CSV.IGNORE_RECORD_LENGTH || CSV.RELAXED) 225 | && CSV.result.length > 0 && CSV.record.length != CSV.result[0].length ){ 226 | CSV.error(CSV.ERROR_EOL); 227 | } 228 | CSV.result.push(CSV.record); 229 | CSV.debug("record end", CSV.record); 230 | CSV.record = null; 231 | }; 232 | 233 | CSV.resolve_type = function (token) { 234 | if( token.match(/^\d+(\.\d+)?$/) ){ 235 | token = parseFloat(token); 236 | } 237 | else if( token.match(/^true|false$/i) ){ 238 | token = Boolean( token.match(/true/i) ); 239 | } 240 | else if(token === "undefined" ){ 241 | token = undefined; 242 | } 243 | else if(token === "null" ){ 244 | token = null; 245 | } 246 | return token; 247 | }; 248 | 249 | CSV.token_begin = function () { 250 | CSV.state = PRE_TOKEN; 251 | // considered using array, but http://www.sitepen.com/blog/2008/05/09/string-performance-an-analysis/ 252 | CSV.token = ""; 253 | }; 254 | 255 | CSV.token_end = function () { 256 | if( CSV.DETECT_TYPES ) { 257 | CSV.token = CSV.resolve_type(CSV.token); 258 | } 259 | CSV.record.push(CSV.token); 260 | CSV.debug("token end", CSV.token); 261 | CSV.token_begin(); 262 | }; 263 | 264 | CSV.debug = function (){ 265 | if( CSV.DEBUG ) 266 | console.log(arguments); 267 | }; 268 | 269 | CSV.dump = function (msg) { 270 | return [ 271 | msg , "at char", CSV.offset, ":", 272 | CSV.str.substr(CSV.offset- 50, 50) 273 | .replace(/\r/mg,"\\r") 274 | .replace(/\n/mg,"\\n") 275 | .replace(/\t/mg,"\\t") 276 | ].join(" "); 277 | }; 278 | 279 | CSV.error = function (err){ 280 | var msg = CSV.dump(err); 281 | CSV.reset(); 282 | throw msg; 283 | }; 284 | 285 | CSV.warn = function (err){ 286 | var msg = CSV.dump(err); 287 | try { 288 | console.warn( msg ); 289 | return; 290 | } catch (e) {} 291 | 292 | try { 293 | console.log( msg ); 294 | } catch (e) {} 295 | 296 | }; 297 | 298 | (function(name, context, definition) { 299 | if (typeof module != 'undefined' && module.exports) module.exports = definition(); 300 | else if (typeof define == 'function' && typeof define.amd == 'object') define(definition); 301 | else this[name] = definition(); 302 | }('CSV', this, function() 303 | { return CSV; } 304 | ) 305 | ); 306 | 307 | })(); 308 | -------------------------------------------------------------------------------- /assets/img/app-icon/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/img/app-icon/icon-60.png -------------------------------------------------------------------------------- /assets/img/app-icon/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/img/app-icon/icon-60@2x.png -------------------------------------------------------------------------------- /assets/img/app-icon/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/img/app-icon/icon-76.png -------------------------------------------------------------------------------- /assets/img/app-icon/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/img/app-icon/icon-76@2x.png -------------------------------------------------------------------------------- /assets/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/img/favicon/favicon.ico -------------------------------------------------------------------------------- /assets/jquery-ui/1.11.0.custom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery UI Example Page 6 | 7 | 50 | 51 | 52 | 53 |

Welcome to jQuery UI!

54 | 55 |
56 |

This page demonstrates the widgets and theme you selected in Download Builder. Please make sure you are using them with a compatible jQuery version.

57 |
58 | 59 |

YOUR COMPONENTS:

60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |

Framework Icons (content color preview)

73 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 |

Highlight / Error

265 |
266 |
267 |

268 | Hey! Sample ui-state-highlight style.

269 |
270 |
271 |
272 |
273 |
274 |

275 | Alert: Sample ui-state-error style.

276 |
277 |
278 | 279 | 280 | 281 | 316 | 317 | 318 | -------------------------------------------------------------------------------- /assets/jquery-ui/1.11.0.custom/jquery-ui.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.0 - 2014-07-31 2 | * http://jqueryui.com 3 | * Includes: core.css, sortable.css 4 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | /* Layout helpers 7 | ----------------------------------*/ 8 | .ui-helper-hidden { 9 | display: none; 10 | } 11 | .ui-helper-hidden-accessible { 12 | border: 0; 13 | clip: rect(0 0 0 0); 14 | height: 1px; 15 | margin: -1px; 16 | overflow: hidden; 17 | padding: 0; 18 | position: absolute; 19 | width: 1px; 20 | } 21 | .ui-helper-reset { 22 | margin: 0; 23 | padding: 0; 24 | border: 0; 25 | outline: 0; 26 | line-height: 1.3; 27 | text-decoration: none; 28 | font-size: 100%; 29 | list-style: none; 30 | } 31 | .ui-helper-clearfix:before, 32 | .ui-helper-clearfix:after { 33 | content: ""; 34 | display: table; 35 | border-collapse: collapse; 36 | } 37 | .ui-helper-clearfix:after { 38 | clear: both; 39 | } 40 | .ui-helper-clearfix { 41 | min-height: 0; /* support: IE7 */ 42 | } 43 | .ui-helper-zfix { 44 | width: 100%; 45 | height: 100%; 46 | top: 0; 47 | left: 0; 48 | position: absolute; 49 | opacity: 0; 50 | filter:Alpha(Opacity=0); 51 | } 52 | 53 | .ui-front { 54 | z-index: 100; 55 | } 56 | 57 | 58 | /* Interaction Cues 59 | ----------------------------------*/ 60 | .ui-state-disabled { 61 | cursor: default !important; 62 | } 63 | 64 | 65 | /* Icons 66 | ----------------------------------*/ 67 | 68 | /* states and images */ 69 | .ui-icon { 70 | display: block; 71 | text-indent: -99999px; 72 | overflow: hidden; 73 | background-repeat: no-repeat; 74 | } 75 | 76 | 77 | /* Misc visuals 78 | ----------------------------------*/ 79 | 80 | /* Overlays */ 81 | .ui-widget-overlay { 82 | position: fixed; 83 | top: 0; 84 | left: 0; 85 | width: 100%; 86 | height: 100%; 87 | } 88 | .ui-sortable-handle { 89 | -ms-touch-action: none; 90 | touch-action: none; 91 | } 92 | -------------------------------------------------------------------------------- /assets/jquery-ui/1.11.0.custom/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.0 - 2014-07-31 2 | * http://jqueryui.com 3 | * Includes: core.css, sortable.css 4 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-sortable-handle{-ms-touch-action:none;touch-action:none} -------------------------------------------------------------------------------- /assets/jquery-ui/1.11.0.custom/jquery-ui.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.0 - 2014-07-31 2 | * http://jqueryui.com 3 | * Includes: core.js, widget.js, mouse.js, position.js, sortable.js 4 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | (function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap=#"+a+"]")[0],!!o&&i(o)):!1):(/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.0",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(){var t=this.css("position"),i="absolute"===t,s=this.parents().filter(function(){var t=e(this);return i&&"static"===t.css("position")?!1:/(auto|scroll)/.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==t&&s.length?s:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&t(i,!n)}}),e("").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var s=0,n=Array.prototype.slice;e.cleanData=function(t){return function(i){for(var s,n=0;null!=(s=i[n]);n++)try{e(s).triggerHandler("remove")}catch(a){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,s,a=n.call(arguments,1),o=0,r=a.length;r>o;o++)for(i in a[o])s=a[o][i],a[o].hasOwnProperty(i)&&void 0!==s&&(t[i]=e.isPlainObject(s)?e.isPlainObject(t[i])?e.widget.extend({},t[i],s):e.widget.extend({},s):s);return t},e.widget.bridge=function(t,i){var s=i.prototype.widgetFullName||t;e.fn[t]=function(a){var o="string"==typeof a,r=n.call(arguments,1),h=this;return a=!o&&r.length?e.widget.extend.apply(null,[a].concat(r)):a,o?this.each(function(){var i,n=e.data(this,s);return"instance"===a?(h=n,!1):n?e.isFunction(n[a])&&"_"!==a.charAt(0)?(i=n[a].apply(n,r),i!==n&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+a+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+a+"'")}):this.each(function(){var t=e.data(this,s);t?(t.option(a||{}),t._init&&t._init()):e.data(this,s,new i(a,this))}),h}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=s++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget;var a=!1;e(document).mouseup(function(){a=!1}),e.widget("ui.mouse",{version:"1.11.0",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!a){this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var i=this,s=1===t.which,n="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return i._mouseMove(e)},this._mouseUpDelegate=function(e){return i._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),a=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):t.which?this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted):this._mouseUp(t)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),a=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function t(e,t,i){return[parseFloat(e[0])*(p.test(e[0])?t/100:1),parseFloat(e[1])*(p.test(e[1])?i/100:1)]}function i(t,i){return parseInt(e.css(t,i),10)||0}function s(t){var i=t[0];return 9===i.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(i)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var n,a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,u=/top|center|bottom/,d=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==n)return n;var t,i,s=e("
"),a=s.children()[0];return e("body").append(s),t=a.offsetWidth,s.css("overflow","scroll"),i=a.offsetWidth,t===i&&(i=s[0].clientWidth),s.remove(),n=t-i},getScrollInfo:function(t){var i=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),s=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),n="scroll"===i||"auto"===i&&t.widthi?"left":t>0?"right":"center",vertical:0>a?"top":s>0?"bottom":"middle"};d>m&&m>r(t+i)&&(h.horizontal="center"),c>g&&g>r(s+a)&&(h.vertical="middle"),h.important=o(r(t),r(i))>o(r(s),r(a))?"horizontal":"vertical",n.using.call(this,e,h)}),u.offset(e.extend(N,{using:l}))})},e.ui.position={fit:{left:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=e.left-t.collisionPosition.marginLeft,h=n-r,l=r+t.collisionWidth-a-n;t.collisionWidth>a?h>0&&0>=l?(i=e.left+h+t.collisionWidth-a-n,e.left+=h-i):e.left=l>0&&0>=h?n:h>l?n+a-t.collisionWidth:n:h>0?e.left+=h:l>0?e.left-=l:e.left=o(e.left-r,e.left)},top:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollTop:s.offset.top,a=t.within.height,r=e.top-t.collisionPosition.marginTop,h=n-r,l=r+t.collisionHeight-a-n;t.collisionHeight>a?h>0&&0>=l?(i=e.top+h+t.collisionHeight-a-n,e.top+=h-i):e.top=l>0&&0>=h?n:h>l?n+a-t.collisionHeight:n:h>0?e.top+=h:l>0?e.top-=l:e.top=o(e.top-r,e.top)}},flip:{left:function(e,t){var i,s,n=t.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=e.left-t.collisionPosition.marginLeft,u=l-h,d=l+t.collisionWidth-o-h,c="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,p="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,f=-2*t.offset[0];0>u?(i=e.left+c+p+f+t.collisionWidth-o-a,(0>i||r(u)>i)&&(e.left+=c+p+f)):d>0&&(s=e.left-t.collisionPosition.marginLeft+c+p+f-h,(s>0||d>r(s))&&(e.left+=c+p+f))},top:function(e,t){var i,s,n=t.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=e.top-t.collisionPosition.marginTop,u=l-h,d=l+t.collisionHeight-o-h,c="top"===t.my[1],p=c?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,f="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,m=-2*t.offset[1];0>u?(s=e.top+p+f+m+t.collisionHeight-o-a,e.top+p+f+m>u&&(0>s||r(u)>s)&&(e.top+=p+f+m)):d>0&&(i=e.top-t.collisionPosition.marginTop+p+f+m-h,e.top+p+f+m>d&&(i>0||d>r(i))&&(e.top+=p+f+m))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,i,s,n,o,r=document.getElementsByTagName("body")[0],h=document.createElement("div");t=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&e.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in s)t.style[o]=s[o];t.appendChild(h),i=r||document.documentElement,i.insertBefore(t,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=e(h).offset().left,a=n>10&&11>n,t.innerHTML="",i.removeChild(t)}()}(),e.ui.position,e.widget("ui.sortable",e.ui.mouse,{version:"1.11.0",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(e,t,i){return e>=t&&t+i>e},_isFloating:function(e){return/left|right/.test(e.css("float"))||/inline|table-cell/.test(e.css("display"))},_create:function(){var e=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===e.axis||this._isFloating(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_setHandleClassName:function(){this.element.find(".ui-sortable-handle").removeClass("ui-sortable-handle"),e.each(this.items,function(){(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item).addClass("ui-sortable-handle")})},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").find(".ui-sortable-handle").removeClass("ui-sortable-handle"),this._mouseDestroy();for(var e=this.items.length-1;e>=0;e--)this.items[e].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(t,i){var s=null,n=!1,a=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(t),e(t.target).parents().each(function(){return e.data(this,a.widgetName+"-item")===a?(s=e(this),!1):void 0}),e.data(t.target,a.widgetName+"-item")===a&&(s=e(t.target)),s?!this.options.handle||i||(e(this.options.handle,s).find("*").addBack().each(function(){this===t.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(t,i,s){var n,a,o=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,o.cursorAt&&this._adjustOffsetFromHelper(o.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),o.containment&&this._setContainment(),o.cursor&&"auto"!==o.cursor&&(a=this.document.find("body"),this.storedCursor=a.css("cursor"),a.css("cursor",o.cursor),this.storedStylesheet=e("").appendTo(a)),o.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",o.opacity)),o.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",o.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",t,this._uiHash(this));return e.ui.ddmanager&&(e.ui.ddmanager.current=this),e.ui.ddmanager&&!o.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(t),!0},_mouseDrag:function(t){var i,s,n,a,o=this.options,r=!1;for(this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY=0;i--)if(s=this.items[i],n=s.item[0],a=this._intersectsWithPointer(s),a&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===a?"next":"prev"]()[0]!==n&&!e.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!e.contains(this.element[0],n):!0)){if(this.direction=1===a?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(t,s),this._trigger("change",t,this._uiHash());break}return this._contactContainers(t),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,i){if(t){if(e.ui.ddmanager&&!this.options.dropBehaviour&&e.ui.ddmanager.drop(this,t),this.options.revert){var s=this,n=this.placeholder.offset(),a=this.options.axis,o={};a&&"x"!==a||(o.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),a&&"y"!==a||(o.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,e(this.helper).animate(o,parseInt(this.options.revert,10)||500,function(){s._clear(t)})}else this._clear(t,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var t=this.containers.length-1;t>=0;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),e.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?e(this.domPosition.prev).after(this.currentItem):e(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(t){var i=this._getItemsAsjQuery(t&&t.connected),s=[];return t=t||{},e(i).each(function(){var i=(e(t.item||this).attr(t.attribute||"id")||"").match(t.expression||/(.+)[\-=_](.+)/);i&&s.push((t.key||i[1]+"[]")+"="+(t.key&&t.expression?i[1]:i[2]))}),!s.length&&t.key&&s.push(t.key+"="),s.join("&")},toArray:function(t){var i=this._getItemsAsjQuery(t&&t.connected),s=[];return t=t||{},i.each(function(){s.push(e(t.item||this).attr(t.attribute||"id")||"")}),s},_intersectsWith:function(e){var t=this.positionAbs.left,i=t+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,a=e.left,o=a+e.width,r=e.top,h=r+e.height,l=this.offset.click.top,u=this.offset.click.left,d="x"===this.options.axis||s+l>r&&h>s+l,c="y"===this.options.axis||t+u>a&&o>t+u,p=d&&c;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>e[this.floating?"width":"height"]?p:t+this.helperProportions.width/2>a&&o>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(e){var t="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top,e.height),i="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left,e.width),s=t&&i,n=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return s?this.floating?a&&"right"===a||"down"===n?2:1:n&&("down"===n?2:1):!1},_intersectsWithSides:function(e){var t=this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top+e.height/2,e.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left+e.width/2,e.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&t||"up"===s&&!t)},_getDragVerticalDirection:function(){var e=this.positionAbs.top-this.lastPositionAbs.top;return 0!==e&&(e>0?"down":"up")},_getDragHorizontalDirection:function(){var e=this.positionAbs.left-this.lastPositionAbs.left;return 0!==e&&(e>0?"right":"left")},refresh:function(e){return this._refreshItems(e),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var e=this.options;return e.connectWith.constructor===String?[e.connectWith]:e.connectWith},_getItemsAsjQuery:function(t){function i(){r.push(this)}var s,n,a,o,r=[],h=[],l=this._connectWith();if(l&&t)for(s=l.length-1;s>=0;s--)for(a=e(l[s]),n=a.length-1;n>=0;n--)o=e.data(a[n],this.widgetFullName),o&&o!==this&&!o.options.disabled&&h.push([e.isFunction(o.options.items)?o.options.items.call(o.element):e(o.options.items,o.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),o]);for(h.push([e.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):e(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return e(r)},_removeCurrentsFromItems:function(){var t=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=e.grep(this.items,function(e){for(var i=0;t.length>i;i++)if(t[i]===e.item[0])return!1;return!0})},_refreshItems:function(t){this.items=[],this.containers=[this];var i,s,n,a,o,r,h,l,u=this.items,d=[[e.isFunction(this.options.items)?this.options.items.call(this.element[0],t,{item:this.currentItem}):e(this.options.items,this.element),this]],c=this._connectWith();if(c&&this.ready)for(i=c.length-1;i>=0;i--)for(n=e(c[i]),s=n.length-1;s>=0;s--)a=e.data(n[s],this.widgetFullName),a&&a!==this&&!a.options.disabled&&(d.push([e.isFunction(a.options.items)?a.options.items.call(a.element[0],t,{item:this.currentItem}):e(a.options.items,a.element),a]),this.containers.push(a));for(i=d.length-1;i>=0;i--)for(o=d[i][1],r=d[i][0],s=0,l=r.length;l>s;s++)h=e(r[s]),h.data(this.widgetName+"-item",o),u.push({item:h,instance:o,width:0,height:0,left:0,top:0})},refreshPositions:function(t){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,a;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?e(this.options.toleranceElement,s.item):s.item,t||(s.width=n.outerWidth(),s.height=n.outerHeight()),a=n.offset(),s.left=a.left,s.top=a.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)a=this.containers[i].element.offset(),this.containers[i].containerCache.left=a.left,this.containers[i].containerCache.top=a.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(t){t=t||this; 7 | var i,s=t.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=t.currentItem[0].nodeName.toLowerCase(),n=e("<"+s+">",t.document[0]).addClass(i||t.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===s?t.currentItem.children().each(function(){e(" ",t.document[0]).attr("colspan",e(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",t.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(e,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10)))}}),t.placeholder=e(s.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),s.placeholder.update(t,t.placeholder)},_contactContainers:function(t){var i,s,n,a,o,r,h,l,u,d,c=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!e.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(c&&e.contains(this.containers[i].element[0],c.element[0]))continue;c=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",t,this._uiHash(this)),this.containers[i].containerCache.over=0);if(c)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,a=null,u=c.floating||this._isFloating(this.currentItem),o=u?"left":"top",r=u?"width":"height",d=u?"clientX":"clientY",s=this.items.length-1;s>=0;s--)e.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[o],l=!1,t[d]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(t[d]-h)&&(n=Math.abs(t[d]-h),a=this.items[s],this.direction=l?"up":"down"));if(!a&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return;a?this._rearrange(t,a,null,!0):this._rearrange(t,null,this.containers[p].element,!0),this._trigger("change",t,this._uiHash()),this.containers[p]._trigger("change",t,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",t,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||e("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.currentItem.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,e("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(e("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(t=e(n.containment)[0],i=e(n.containment).offset(),s="hidden"!==e(t).css("overflow"),this.containment=[i.left+(parseInt(e(t).css("borderLeftWidth"),10)||0)+(parseInt(e(t).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(e(t).css("borderTopWidth"),10)||0)+(parseInt(e(t).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(t.scrollWidth,t.offsetWidth):t.offsetWidth)-(parseInt(e(t).css("borderLeftWidth"),10)||0)-(parseInt(e(t).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(t.scrollHeight,t.offsetHeight):t.offsetHeight)-(parseInt(e(t).css("borderTopWidth"),10)||0)-(parseInt(e(t).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(t,i){i||(i=this.position);var s="absolute"===t?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,a=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():a?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():a?0:n.scrollLeft())*s}},_generatePosition:function(t){var i,s,n=this.options,a=t.pageX,o=t.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(t.pageX-this.offset.click.leftthis.containment[2]&&(a=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(o=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1],o=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((a-this.originalPageX)/n.grid[0])*n.grid[0],a=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:o-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:a-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(e,t,i,s){i?i[0].appendChild(this.placeholder[0]):t.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?t.item[0]:t.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(e,t){function i(e,t,i){return function(s){i._trigger(e,s,t._uiHash(t))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!t&&n.push(function(e){this._trigger("receive",e,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||t||n.push(function(e){this._trigger("update",e,this._uiHash())}),this!==this.currentContainer&&(t||(n.push(function(e){this._trigger("remove",e,this._uiHash())}),n.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)t||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!t){for(this._trigger("beforeStop",e,this._uiHash()),s=0;n.length>s;s++)n[s].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!1}if(t||this._trigger("beforeStop",e,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!t){for(s=0;n.length>s;s++)n[s].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){e.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(t){var i=t||this;return{helper:i.helper,placeholder:i.placeholder||e([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:t?t.element:null}}})}); -------------------------------------------------------------------------------- /assets/jquery-ui/1.11.0.custom/jquery-ui.structure.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.11.0 3 | * http://jqueryui.com 4 | * 5 | * Copyright 2014 jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | 12 | /* Layout helpers 13 | ----------------------------------*/ 14 | .ui-helper-hidden { 15 | display: none; 16 | } 17 | .ui-helper-hidden-accessible { 18 | border: 0; 19 | clip: rect(0 0 0 0); 20 | height: 1px; 21 | margin: -1px; 22 | overflow: hidden; 23 | padding: 0; 24 | position: absolute; 25 | width: 1px; 26 | } 27 | .ui-helper-reset { 28 | margin: 0; 29 | padding: 0; 30 | border: 0; 31 | outline: 0; 32 | line-height: 1.3; 33 | text-decoration: none; 34 | font-size: 100%; 35 | list-style: none; 36 | } 37 | .ui-helper-clearfix:before, 38 | .ui-helper-clearfix:after { 39 | content: ""; 40 | display: table; 41 | border-collapse: collapse; 42 | } 43 | .ui-helper-clearfix:after { 44 | clear: both; 45 | } 46 | .ui-helper-clearfix { 47 | min-height: 0; /* support: IE7 */ 48 | } 49 | .ui-helper-zfix { 50 | width: 100%; 51 | height: 100%; 52 | top: 0; 53 | left: 0; 54 | position: absolute; 55 | opacity: 0; 56 | filter:Alpha(Opacity=0); 57 | } 58 | 59 | .ui-front { 60 | z-index: 100; 61 | } 62 | 63 | 64 | /* Interaction Cues 65 | ----------------------------------*/ 66 | .ui-state-disabled { 67 | cursor: default !important; 68 | } 69 | 70 | 71 | /* Icons 72 | ----------------------------------*/ 73 | 74 | /* states and images */ 75 | .ui-icon { 76 | display: block; 77 | text-indent: -99999px; 78 | overflow: hidden; 79 | background-repeat: no-repeat; 80 | } 81 | 82 | 83 | /* Misc visuals 84 | ----------------------------------*/ 85 | 86 | /* Overlays */ 87 | .ui-widget-overlay { 88 | position: fixed; 89 | top: 0; 90 | left: 0; 91 | width: 100%; 92 | height: 100%; 93 | } 94 | .ui-sortable-handle { 95 | -ms-touch-action: none; 96 | touch-action: none; 97 | } 98 | -------------------------------------------------------------------------------- /assets/jquery-ui/1.11.0.custom/jquery-ui.structure.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.0 - 2014-07-31 2 | * http://jqueryui.com 3 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-sortable-handle{-ms-touch-action:none;touch-action:none} -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/fonts/basic.icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/fonts/basic.icons.eot -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/fonts/basic.icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/fonts/basic.icons.ttf -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/fonts/basic.icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/fonts/basic.icons.woff -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/fonts/icons.eot -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/fonts/icons.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/fonts/icons.otf -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/fonts/icons.ttf -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/fonts/icons.woff -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/images/loader-large-inverted.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/images/loader-large-inverted.gif -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/images/loader-large.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/images/loader-large.gif -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/images/loader-medium-inverted.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/images/loader-medium-inverted.gif -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/images/loader-medium.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/images/loader-medium.gif -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/images/loader-mini-inverted.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/images/loader-mini-inverted.gif -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/images/loader-mini.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/images/loader-mini.gif -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/images/loader-small-inverted.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/images/loader-small-inverted.gif -------------------------------------------------------------------------------- /assets/semantic-ui/0.19.0/images/loader-small.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfoldr/hackfoldr-2.0-forkme/85dcf49da02037acf20a08c53e3c3e2899ffe09b/assets/semantic-ui/0.19.0/images/loader-small.gif -------------------------------------------------------------------------------- /assets/tabletop/tabletop.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | "use strict"; 3 | 4 | var inNodeJS = false; 5 | if (typeof module !== 'undefined' && module.exports) { 6 | inNodeJS = true; 7 | var request = require('request'); 8 | } 9 | 10 | var supportsCORS = false; 11 | var inLegacyIE = false; 12 | try { 13 | var testXHR = new XMLHttpRequest(); 14 | if (typeof testXHR.withCredentials !== 'undefined') { 15 | supportsCORS = true; 16 | } else { 17 | if ("XDomainRequest" in window) { 18 | supportsCORS = true; 19 | inLegacyIE = true; 20 | } 21 | } 22 | } catch (e) { } 23 | 24 | // Create a simple indexOf function for support 25 | // of older browsers. Uses native indexOf if 26 | // available. Code similar to underscores. 27 | // By making a separate function, instead of adding 28 | // to the prototype, we will not break bad for loops 29 | // in older browsers 30 | var indexOfProto = Array.prototype.indexOf; 31 | var ttIndexOf = function(array, item) { 32 | var i = 0, l = array.length; 33 | 34 | if (indexOfProto && array.indexOf === indexOfProto) return array.indexOf(item); 35 | for (; i < l; i++) if (array[i] === item) return i; 36 | return -1; 37 | }; 38 | 39 | /* 40 | Initialize with Tabletop.init( { key: '0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc' } ) 41 | OR! 42 | Initialize with Tabletop.init( { key: 'https://docs.google.com/spreadsheet/pub?hl=en_US&hl=en_US&key=0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc&output=html&widget=true' } ) 43 | OR! 44 | Initialize with Tabletop.init('0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc') 45 | */ 46 | 47 | var Tabletop = function(options) { 48 | // Make sure Tabletop is being used as a constructor no matter what. 49 | if(!this || !(this instanceof Tabletop)) { 50 | return new Tabletop(options); 51 | } 52 | 53 | if(typeof(options) === 'string') { 54 | options = { key : options }; 55 | } 56 | 57 | this.callback = options.callback; 58 | this.wanted = options.wanted || []; 59 | this.key = options.key; 60 | this.simpleSheet = !!options.simpleSheet; 61 | this.parseNumbers = !!options.parseNumbers; 62 | this.wait = !!options.wait; 63 | this.reverse = !!options.reverse; 64 | this.postProcess = options.postProcess; 65 | this.debug = !!options.debug; 66 | this.query = options.query || ''; 67 | this.orderby = options.orderby; 68 | this.endpoint = options.endpoint || "https://spreadsheets.google.com"; 69 | this.singleton = !!options.singleton; 70 | this.simple_url = !!options.simple_url; 71 | this.callbackContext = options.callbackContext; 72 | 73 | if(typeof(options.proxy) !== 'undefined') { 74 | // Remove trailing slash, it will break the app 75 | this.endpoint = options.proxy.replace(/\/$/,''); 76 | this.simple_url = true; 77 | this.singleton = true; 78 | // Let's only use CORS (straight JSON request) when 79 | // fetching straight from Google 80 | supportsCORS = false 81 | } 82 | 83 | this.parameterize = options.parameterize || false; 84 | 85 | if(this.singleton) { 86 | if(typeof(Tabletop.singleton) !== 'undefined') { 87 | this.log("WARNING! Tabletop singleton already defined"); 88 | } 89 | Tabletop.singleton = this; 90 | } 91 | 92 | /* Be friendly about what you accept */ 93 | if(/key=/.test(this.key)) { 94 | this.log("You passed an old Google Docs url as the key! Attempting to parse."); 95 | this.key = this.key.match("key=(.*?)(&|#|$)")[1]; 96 | } 97 | 98 | if(/pubhtml/.test(this.key)) { 99 | this.log("You passed a new Google Spreadsheets url as the key! Attempting to parse."); 100 | this.key = this.key.match("d\\/(.*?)\\/pubhtml")[1]; 101 | } 102 | 103 | if(!this.key) { 104 | this.log("You need to pass Tabletop a key!"); 105 | return; 106 | } 107 | 108 | this.log("Initializing with key " + this.key); 109 | 110 | this.models = {}; 111 | this.model_names = []; 112 | 113 | this.base_json_path = "/feeds/worksheets/" + this.key + "/public/basic?alt="; 114 | 115 | if (inNodeJS || supportsCORS) { 116 | this.base_json_path += 'json'; 117 | } else { 118 | this.base_json_path += 'json-in-script'; 119 | } 120 | 121 | if(!this.wait) { 122 | this.fetch(); 123 | } 124 | }; 125 | 126 | // A global storage for callbacks. 127 | Tabletop.callbacks = {}; 128 | 129 | // Backwards compatibility. 130 | Tabletop.init = function(options) { 131 | return new Tabletop(options); 132 | }; 133 | 134 | Tabletop.sheets = function() { 135 | this.log("Times have changed! You'll want to use var tabletop = Tabletop.init(...); tabletop.sheets(...); instead of Tabletop.sheets(...)"); 136 | }; 137 | 138 | Tabletop.prototype = { 139 | 140 | fetch: function(callback) { 141 | if(typeof(callback) !== "undefined") { 142 | this.callback = callback; 143 | } 144 | this.requestData(this.base_json_path, this.loadSheets); 145 | }, 146 | 147 | /* 148 | This will call the environment appropriate request method. 149 | 150 | In browser it will use JSON-P, in node it will use request() 151 | */ 152 | requestData: function(path, callback) { 153 | if (inNodeJS) { 154 | this.serverSideFetch(path, callback); 155 | } else { 156 | //CORS only works in IE8/9 across the same protocol 157 | //You must have your server on HTTPS to talk to Google, or it'll fall back on injection 158 | var protocol = this.endpoint.split("//").shift() || "http"; 159 | if (supportsCORS && (!inLegacyIE || protocol === location.protocol)) { 160 | this.xhrFetch(path, callback); 161 | } else { 162 | this.injectScript(path, callback); 163 | } 164 | } 165 | }, 166 | 167 | /* 168 | Use Cross-Origin XMLHttpRequest to get the data in browsers that support it. 169 | */ 170 | xhrFetch: function(path, callback) { 171 | //support IE8's separate cross-domain object 172 | var xhr = inLegacyIE ? new XDomainRequest() : new XMLHttpRequest(); 173 | xhr.open("GET", this.endpoint + path); 174 | var self = this; 175 | xhr.onload = function() { 176 | try { 177 | var json = JSON.parse(xhr.responseText); 178 | } catch (e) { 179 | console.error(e); 180 | } 181 | callback.call(self, json); 182 | }; 183 | xhr.send(); 184 | }, 185 | 186 | /* 187 | Insert the URL into the page as a script tag. Once it's loaded the spreadsheet data 188 | it triggers the callback. This helps you avoid cross-domain errors 189 | http://code.google.com/apis/gdata/samples/spreadsheet_sample.html 190 | 191 | Let's be plain-Jane and not use jQuery or anything. 192 | */ 193 | injectScript: function(path, callback) { 194 | var script = document.createElement('script'); 195 | var callbackName; 196 | 197 | if(this.singleton) { 198 | if(callback === this.loadSheets) { 199 | callbackName = 'Tabletop.singleton.loadSheets'; 200 | } else if (callback === this.loadSheet) { 201 | callbackName = 'Tabletop.singleton.loadSheet'; 202 | } 203 | } else { 204 | var self = this; 205 | callbackName = 'tt' + (+new Date()) + (Math.floor(Math.random()*100000)); 206 | // Create a temp callback which will get removed once it has executed, 207 | // this allows multiple instances of Tabletop to coexist. 208 | Tabletop.callbacks[ callbackName ] = function () { 209 | var args = Array.prototype.slice.call( arguments, 0 ); 210 | callback.apply(self, args); 211 | script.parentNode.removeChild(script); 212 | delete Tabletop.callbacks[callbackName]; 213 | }; 214 | callbackName = 'Tabletop.callbacks.' + callbackName; 215 | } 216 | 217 | var url = path + "&callback=" + callbackName; 218 | 219 | if(this.simple_url) { 220 | // We've gone down a rabbit hole of passing injectScript the path, so let's 221 | // just pull the sheet_id out of the path like the least efficient worker bees 222 | if(path.indexOf("/list/") !== -1) { 223 | script.src = this.endpoint + "/" + this.key + "-" + path.split("/")[4]; 224 | } else { 225 | script.src = this.endpoint + "/" + this.key; 226 | } 227 | } else { 228 | script.src = this.endpoint + url; 229 | } 230 | 231 | if (this.parameterize) { 232 | script.src = this.parameterize + encodeURIComponent(script.src); 233 | } 234 | 235 | document.getElementsByTagName('script')[0].parentNode.appendChild(script); 236 | }, 237 | 238 | /* 239 | This will only run if tabletop is being run in node.js 240 | */ 241 | serverSideFetch: function(path, callback) { 242 | var self = this 243 | request({url: this.endpoint + path, json: true}, function(err, resp, body) { 244 | if (err) { 245 | return console.error(err); 246 | } 247 | callback.call(self, body); 248 | }); 249 | }, 250 | 251 | /* 252 | Is this a sheet you want to pull? 253 | If { wanted: ["Sheet1"] } has been specified, only Sheet1 is imported 254 | Pulls all sheets if none are specified 255 | */ 256 | isWanted: function(sheetName) { 257 | if(this.wanted.length === 0) { 258 | return true; 259 | } else { 260 | return (ttIndexOf(this.wanted, sheetName) !== -1); 261 | } 262 | }, 263 | 264 | /* 265 | What gets send to the callback 266 | if simpleSheet === true, then don't return an array of Tabletop.this.models, 267 | only return the first one's elements 268 | */ 269 | data: function() { 270 | // If the instance is being queried before the data's been fetched 271 | // then return undefined. 272 | if(this.model_names.length === 0) { 273 | return undefined; 274 | } 275 | if(this.simpleSheet) { 276 | if(this.model_names.length > 1 && this.debug) { 277 | this.log("WARNING You have more than one sheet but are using simple sheet mode! Don't blame me when something goes wrong."); 278 | } 279 | return this.models[ this.model_names[0] ].all(); 280 | } else { 281 | return this.models; 282 | } 283 | }, 284 | 285 | /* 286 | Add another sheet to the wanted list 287 | */ 288 | addWanted: function(sheet) { 289 | if(ttIndexOf(this.wanted, sheet) === -1) { 290 | this.wanted.push(sheet); 291 | } 292 | }, 293 | 294 | /* 295 | Load all worksheets of the spreadsheet, turning each into a Tabletop Model. 296 | Need to use injectScript because the worksheet view that you're working from 297 | doesn't actually include the data. The list-based feed (/feeds/list/key..) does, though. 298 | Calls back to loadSheet in order to get the real work done. 299 | 300 | Used as a callback for the worksheet-based JSON 301 | */ 302 | loadSheets: function(data) { 303 | var i, ilen; 304 | var toLoad = []; 305 | this.foundSheetNames = []; 306 | 307 | for(i = 0, ilen = data.feed.entry.length; i < ilen ; i++) { 308 | this.foundSheetNames.push(data.feed.entry[i].title.$t); 309 | // Only pull in desired sheets to reduce loading 310 | if( this.isWanted(data.feed.entry[i].content.$t) ) { 311 | var linkIdx = data.feed.entry[i].link.length-1; 312 | var sheet_id = data.feed.entry[i].link[linkIdx].href.split('/').pop(); 313 | var json_path = "/feeds/list/" + this.key + "/" + sheet_id + "/public/values?alt=" 314 | if (inNodeJS || supportsCORS) { 315 | json_path += 'json'; 316 | } else { 317 | json_path += 'json-in-script'; 318 | } 319 | if(this.query) { 320 | json_path += "&sq=" + this.query; 321 | } 322 | if(this.orderby) { 323 | json_path += "&orderby=column:" + this.orderby.toLowerCase(); 324 | } 325 | if(this.reverse) { 326 | json_path += "&reverse=true"; 327 | } 328 | toLoad.push(json_path); 329 | } 330 | } 331 | 332 | this.sheetsToLoad = toLoad.length; 333 | for(i = 0, ilen = toLoad.length; i < ilen; i++) { 334 | this.requestData(toLoad[i], this.loadSheet); 335 | } 336 | }, 337 | 338 | /* 339 | Access layer for the this.models 340 | .sheets() gets you all of the sheets 341 | .sheets('Sheet1') gets you the sheet named Sheet1 342 | */ 343 | sheets: function(sheetName) { 344 | if(typeof sheetName === "undefined") { 345 | return this.models; 346 | } else { 347 | if(typeof(this.models[ sheetName ]) === "undefined") { 348 | // alert( "Can't find " + sheetName ); 349 | return; 350 | } else { 351 | return this.models[ sheetName ]; 352 | } 353 | } 354 | }, 355 | 356 | /* 357 | Parse a single list-based worksheet, turning it into a Tabletop Model 358 | 359 | Used as a callback for the list-based JSON 360 | */ 361 | loadSheet: function(data) { 362 | var model = new Tabletop.Model( { data: data, 363 | parseNumbers: this.parseNumbers, 364 | postProcess: this.postProcess, 365 | tabletop: this } ); 366 | this.models[ model.name ] = model; 367 | if(ttIndexOf(this.model_names, model.name) === -1) { 368 | this.model_names.push(model.name); 369 | } 370 | this.sheetsToLoad--; 371 | if(this.sheetsToLoad === 0) 372 | this.doCallback(); 373 | }, 374 | 375 | /* 376 | Execute the callback upon loading! Rely on this.data() because you might 377 | only request certain pieces of data (i.e. simpleSheet mode) 378 | Tests this.sheetsToLoad just in case a race condition happens to show up 379 | */ 380 | doCallback: function() { 381 | if(this.sheetsToLoad === 0) { 382 | this.callback.apply(this.callbackContext || this, [this.data(), this]); 383 | } 384 | }, 385 | 386 | log: function(msg) { 387 | if(this.debug) { 388 | if(typeof console !== "undefined" && typeof console.log !== "undefined") { 389 | Function.prototype.apply.apply(console.log, [console, arguments]); 390 | } 391 | } 392 | } 393 | 394 | }; 395 | 396 | /* 397 | Tabletop.Model stores the attribute names and parses the worksheet data 398 | to turn it into something worthwhile 399 | 400 | Options should be in the format { data: XXX }, with XXX being the list-based worksheet 401 | */ 402 | Tabletop.Model = function(options) { 403 | var i, j, ilen, jlen; 404 | this.column_names = []; 405 | this.name = options.data.feed.title.$t; 406 | this.elements = []; 407 | this.raw = options.data; // A copy of the sheet's raw data, for accessing minutiae 408 | 409 | if(typeof(options.data.feed.entry) === 'undefined') { 410 | options.tabletop.log("Missing data for " + this.name + ", make sure you didn't forget column headers"); 411 | this.elements = []; 412 | return; 413 | } 414 | 415 | for(var key in options.data.feed.entry[0]){ 416 | if(/^gsx/.test(key)) 417 | this.column_names.push( key.replace("gsx$","") ); 418 | } 419 | 420 | for(i = 0, ilen = options.data.feed.entry.length ; i < ilen; i++) { 421 | var source = options.data.feed.entry[i]; 422 | var element = {}; 423 | for(var j = 0, jlen = this.column_names.length; j < jlen ; j++) { 424 | var cell = source[ "gsx$" + this.column_names[j] ]; 425 | if (typeof(cell) !== 'undefined') { 426 | if(options.parseNumbers && cell.$t !== '' && !isNaN(cell.$t)) 427 | element[ this.column_names[j] ] = +cell.$t; 428 | else 429 | element[ this.column_names[j] ] = cell.$t; 430 | } else { 431 | element[ this.column_names[j] ] = ''; 432 | } 433 | } 434 | if(element.rowNumber === undefined) 435 | element.rowNumber = i + 1; 436 | if( options.postProcess ) 437 | options.postProcess(element); 438 | this.elements.push(element); 439 | } 440 | 441 | }; 442 | 443 | Tabletop.Model.prototype = { 444 | /* 445 | Returns all of the elements (rows) of the worksheet as objects 446 | */ 447 | all: function() { 448 | return this.elements; 449 | }, 450 | 451 | /* 452 | Return the elements as an array of arrays, instead of an array of objects 453 | */ 454 | toArray: function() { 455 | var array = [], 456 | i, j, ilen, jlen; 457 | for(i = 0, ilen = this.elements.length; i < ilen; i++) { 458 | var row = []; 459 | for(j = 0, jlen = this.column_names.length; j < jlen ; j++) { 460 | row.push( this.elements[i][ this.column_names[j] ] ); 461 | } 462 | array.push(row); 463 | } 464 | return array; 465 | } 466 | }; 467 | 468 | if(inNodeJS) { 469 | module.exports = Tabletop; 470 | } else { 471 | global.Tabletop = Tabletop; 472 | } 473 | 474 | })(this); 475 | -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e -x 3 | git --version 4 | upstream=$1 5 | : ${upstream:=origin} 6 | : ${REPO:=git@github.com:hackfoldr/hackfoldr-2.0} 7 | git fetch $upstream 8 | if [ `git rev-list HEAD...$upstream/master --count` -ne 0 ]; then 9 | echo "not deploying" 10 | exit 1 11 | fi 12 | npm i 13 | rm -rf _public 14 | # XXX: use --reference when not in shallow clone 15 | #git clone $REPO --reference . -b gh-pages _public 16 | git clone $REPO -b gh-pages _public 17 | rm -rf _public/css 18 | REV=`git describe --always` 19 | BUILD=git-$REV ./node_modules/.bin/gulp build 20 | cd _public 21 | git add -A . 22 | echo "regen for $REV" | git commit-tree `git write-tree` -p `git rev-parse HEAD` -p $REV | xargs git reset --hard 23 | git push origin gh-pages 24 | cd .. 25 | -------------------------------------------------------------------------------- /deploy.bat: -------------------------------------------------------------------------------- 1 | for %%X in (git.exe) do (set FOUND=%%~$PATH:X) 2 | if not defined FOUND goto NOGIT 3 | 4 | git checkout gh-pages 5 | git merge master 6 | call jade views/{index,404}.jade -o . -pretty 7 | git add . 8 | git commit -m 'deploy' 9 | git pull 10 | git push 11 | git checkout master 12 | pause 13 | exit 0 14 | 15 | :NOGIT 16 | echo Can't find Git! 17 | pause 18 | exit 1 19 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | git checkout gh-pages 2 | git merge master 3 | jade views/{index,404}.jade -o . -pretty 4 | git add . 5 | git commit -m 'deploy' 6 | git pull 7 | git push 8 | git checkout master 9 | -------------------------------------------------------------------------------- /docs/Developing Hackfoldr 2.0 zh-tw.md: -------------------------------------------------------------------------------- 1 | Developing Hackfoldr 2.0 2 | === 3 | 4 | ## 使用 master branch 開發 5 | 6 | 如果你想要用 jade、sass 以比較有效率的方式編輯源碼,可回 master branch,但就需要另外設定開發環境。有兩種環境同時都可以作用 7 | 8 | ### 前製作業 9 | 10 | 不管使用以下哪種開發環境,都要安裝 [node.js](http://nodejs.org/) 11 | 12 | ### 開發環境 13 | 14 | #### fire.app 15 | 16 | 可參考 [Fire.app Jade Windows](https://g0v.hackpad.com/FK7eBR4BdAj) 17 | 18 | 1. 安裝 fire.app 19 | 2. 安裝 jade 20 | 21 | #### gulp.js 22 | 23 | 1. 安裝 ruby 24 | 25 | - mac / linux 26 | 27 | rvm install 2.0.0 28 | 29 | - windows 30 | 31 | [rubyuinstaller]( rubyuinstaller) 32 | 33 | 2. 安裝 compass 34 | 35 | gem install compass 36 | 37 | 3. 安裝其他專案需要的東西 38 | 39 | npm i 40 | 41 | 4. 啟動 local server 42 | 43 | npm start 44 | 45 | 5. 預覽 46 | 47 | http://localhost:3000/ 48 | 49 | 50 | ### 自動 deploy 51 | 52 | 在 master 開發完後需要 deploy 到 gh-pages 才會生效。 53 | 54 | deploy 的方法有兩種: 55 | 56 | 1. 在 master 產生出 .html .css 後,手動下 git 指令 merge 到 gh-pages(it works,但不推薦) 57 | 58 | 2. 讓程式自動幫我們跑 deploy 指令(it works smart,推薦) 59 | 60 | 61 | 兩種 deploy 方式中,自動 deploy 又分兩種: 62 | 63 | 1. 半自動 deploy script 64 | 65 | 2. 全自動 travis ci 66 | 67 | 目前 hackfoldr 2.0 是採用 travis ci 做全自動 deploy,但筆者不會用,所以只介紹半自動 script ... 68 | 69 | #### 半自動 deploy script 70 | 71 | - windows 72 | 73 | deploy.bat 74 | 75 | - mac 76 | 77 | deploy.sh 78 | 79 | #### 全自動 travis ci 80 | 81 | 想要使用 travis ci 的話,repo 裡的 travis ci 的 key 要改成你自己的才會生效。替你的 repo 設定 travis ci 需要做以下步驟: 82 | 83 | 參考 comment 生成 key 84 | https://github.com/hackfoldr/hackfoldr-2.0/blob/master/.travis/after_success.sh 85 | 86 | ( [Lee](https://g0v.hackpad.com/ep/profile/v6ozRKwVLwr) 待補) 87 | -------------------------------------------------------------------------------- /docs/Hosting your own Hackfoldr 2.0 zh-tw.md: -------------------------------------------------------------------------------- 1 | Hackfoldr 2.0 教學 - 架設自己的 Hackfoldr 2 | === 3 | 4 | *In other languages:* 5 | 6 | - [English tutorial](https://github.com/hackfoldr/hackfoldr-2.0-forkme/blob/master/docs/Hosting%20your%20own%20Hackfoldr%202.0.md) 7 | - [中文版](https://github.com/hackfoldr/hackfoldr-2.0-forkme/blob/master/docs/Hosting%20your%20own%20Hackfoldr%202.0%20zh-tw.md) 8 | 9 | 10 | ## Hackfoldr 是什麼 11 | 12 | Hackfoldr 讓你在協作開源專案時,方便收集散落網路各地的文件。 13 | 14 | 範例 folder: http://beta.hackfoldr.org/hackfolder_template 15 | 16 | ### 如何運作 17 | 18 | Hackfoldr 使用 ethercalc.org 的線上表單來動態收集內容,表單中的每一行都代表一個連結。你還可以設定連結要開新分頁、子資料夾、或標上彩色標籤。 19 | 20 | ## 白牌打包 21 | 22 | ### 自架 hackfoldr 的好處 23 | 24 | - 使用自己的網址,自訂 folder 首頁的預設內容 25 | 26 | 例如: http://beta.hackfoldr.org/ 連結到 ethercalc.org/welcome-to-hackfoldr 27 | 而 http://folder.moztw.org/ 連結到 ethercalc.org/moztw ,收集所有關於 MozTW 社群的文件 28 | 29 | - 可打開隱藏的快捷選單,把常用連結內建到 hackfoldr 導覽列 30 | - 可自訂關聯的 hackpad 站台 31 | 32 | ## 步驟 ## 33 | 34 | ### 第零步 35 | 36 | 你需要下列東西以繼續架設自己的 hackfoldr 37 | 38 | - 準備好一個網址 39 | - 擁有一個 github 帳號 40 | 41 | ### fork github repo 42 | 43 | 1. 到 https://github.com/hackfoldr/hackfoldr-2.0-forkme 44 | 45 | 2. 按右上角的 Fork 按鈕 46 | 47 | ### 修改 CNAME 檔案 48 | 49 | 1. 先打開你剛 fork 的全新 github 專案頁面 50 | 51 | 2. 從左上的 Branch 下拉選單,切換到 `gh-pages` 分支 52 | 53 | 我們使用 github 的 project page,而不是 user page 或 organization page,所以顯示成頁面的分支是 gh-pages,而不是 master 54 | ![](imgs/gh-pages-branch.png) 55 | 56 | 3. 點開根目錄的 CNAME 檔案 57 | 58 | 4. 點選右上角的「鉛筆」按鈕編輯 CNAME 檔案內容 59 | 60 | 5. 將內容改成你欲使用的網址 61 | 62 | 請確定此檔案內只包含單一行網址,範例: https://github.com/hackfoldr/hackfoldr-2.0/blob/gh-pages/CNAME 63 | 64 | 65 | ### 使用域名管理服務 (DNS) 設定網址 66 | 67 | - 例如 amazon 的 router 53 68 | 69 | name: _YOUR_DOMAIN_NAME_. 70 | type: CNAME 71 | value: _YOUR_GITHUB_REPO_NAME_.github.io 72 | evaluate target: - 73 | health check id: - 74 | ttl: 300 75 | 76 | 範例 77 | 78 | name: hack.etblue.tw. 79 | type: CNAME 80 | value: etblue.github.io 81 | evaluate target: - 82 | health check id: - 83 | ttl: 300 84 | 85 | - 假若是 cloudflare 86 | 87 | - 設定 `Cname` 到 `etblue.github.io` 88 | - 設定 `Page Rule`, 89 | `http://etblue.github.io/+` forward到 `http://etblue.github.io` 90 | 91 | [Cloudflare guide]( http://blog.cloudflare.com/introducing-pagerules-url-forwarding/) 92 | 93 | 94 | ### 自訂你的 hackfoldr 95 | 96 | 1. 複製 [ethercalc 範例表單](https://ethercalc.org/hackfolder_template) 到一個你喜歡的新網址去 97 | 98 | 2. 打開你的 repo,切換到 `gh-pages` 分支 99 | 100 | 3. 打開 `index.html` 檔案,按鉛筆按鈕編輯 101 | 102 | 畫面會類似這樣: https://github.com/hackfoldr/hackfoldr-2.0/blob/gh-pages/index.html 103 | 104 | 4. 依樣畫葫蘆做這幾件事情 105 | 106 | - 設定你的 [defult foldr id 指向到新的 etherpad 網址](https://github.com/moztw/hackfoldr-moztw/commit/73f712e028f7dd446750dde4aa9e90cda4a48bda) 107 | 108 | **重要!** 請務必要設定自己的預設 folder,不要直接把 welcome-to-hackfoldr 這份 ethercalc 的資料改掉,不然大家都沒有教學文件可以看啦 >_< 109 | 110 | - 設定你的 [Github repo issue 回報處](https://github.com/moztw/hackfoldr-moztw/commit/a08f238e2e32b61273827943b1d3b4f5f21c67ab) 111 | 112 | - 設定[預設使用的 hackpad 站台](https://github.com/moztw/hackfoldr-moztw/commit/ccb57c3541c2ba370161bae7a3683a99a861dfe4) 113 | 114 | - [將剛才在 index.html 的修改全數複製到 404.html](https://github.com/moztw/hackfoldr-moztw/commit/dba706726b0cb0004e74ad9ff5cf9a816367deb8)(直接整份檔案複製貼上即可) 115 | 116 | - 在 Github repo 的「Settings」中,啟動「Enforce HTTPS」功能,以保護用戶隱私 117 | 118 | ### 完成了! 119 | 120 | 現在只需要耐心等待域名生效。一般來說不會太久,十分鐘之類的,除非 TTL 設定成超長時間。 121 | 122 | ## 開發 123 | 124 | 請參考 [這份文件](https://github.com/hackfoldr/hackfoldr-2.0-forkme/blob/master/docs/Developing%20Hackfoldr%202.0%20zh-tw.md) 或 [README](https://github.com/hackfoldr/hackfoldr-2.0-forkme/blob/master/README.md) 來貢獻本專案。 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/Hosting your own Hackfoldr 2.0.md: -------------------------------------------------------------------------------- 1 | Tutorial: Hosting your own Hackfoldr 2.0 2 | === 3 | 4 | *In other languages:* 5 | 6 | - [English tutorial](https://github.com/hackfoldr/hackfoldr-2.0-forkme/blob/master/docs/Hosting%20your%20own%20Hackfoldr%202.0.md) 7 | - [中文版](https://github.com/hackfoldr/hackfoldr-2.0-forkme/blob/master/docs/Hosting%20your%20own%20Hackfoldr%202.0%20zh-tw.md) 8 | 9 | ## What is Hackfoldr 2.0 10 | 11 | Hackfoldr is a convenience web folder to store links for your collaborative open source projects. 12 | 13 | A sample folder - http://beta.hackfoldr.org/hackfolder_template 14 | 15 | ### How does it work 16 | 17 | Each hackfoldr uses an ethercalc.org spreadsheet as its live configuration file. Each row of the spreadsheet represents a link. You can also set each link with supplementary properties. For example, to open in a new window, act as a subfolder, or tag with colored labels. 18 | 19 | ## Hosting your own Hackfoldr 20 | 21 | *It's as easy as fork & customize* 22 | 23 | ### Benefit 24 | 25 | - Use your own domain name and customize the landing page. 26 | 27 | For example, http://beta.hackfoldr.org/ linked to ethercalc.org/welcome-to-hackfoldr 28 | 29 | And http://folder.moztw.org/ linked to ethercalc.org/moztw, which stored all docs related to MozTW, Mozilla Taiwan Community. 30 | 31 | - You will be able to enable the hidden shortcut menu on your own instance. 32 | 33 | ## Steps 34 | 35 | ### Step 0: Getting Started 36 | 37 | You will need the following to proceed: 38 | 39 | - A domain name 40 | - A github account 41 | 42 | ### Step 1: Fork Hackfoldr 2.0 repo 43 | 44 | 1. Go to https://github.com/hackfoldr/hackfoldr-2.0-forkme 45 | 46 | 2. Click the top right “fork” button 47 | 48 | ### Step 2: Edit `CNAME` file in gh-pages branch 49 | 50 | 1. Go to your shiny new repo 51 | 52 | 2. Switch to `gh-pages` branch at top left branch dropdown button 53 | ![](imgs/gh-pages-branch.png) 54 | 55 | 3. Click and open CNAME file in root directory 56 | 57 | 4. Click top right “pencil” button to edit the CNAME file 58 | 59 | 5. Put your domain in the CNAME file 60 | 61 | Make sure that CNAME file contains only One Single Line, [example](https://github.com/hackfoldr/hackfoldr-2.0/blob/gh-pages/CNAME) 62 | 63 | 64 | ### Step 3: Set up your DNS 65 | 66 | Add a CNAME record of your domain, and set the destination to `_YOUR_GITHUB_ACCOUNT_.github.io` 67 | 68 | - Example for Amazon Router 53 69 | 70 | name: _YOUR_DOMAIN_NAME_. 71 | type: CNAME 72 | value: _YOUR_GITHUB_REPO_NAME_.github.io 73 | evaluate target: - 74 | health check id: - 75 | ttl: 300 76 | 77 | Example, 78 | 79 | name: folder.moztw.org. 80 | type: CNAME 81 | value: moztw.github.io 82 | evaluate target: - 83 | health check id: - 84 | ttl: 300 85 | 86 | 87 | - Example for Cloudflare 88 | 89 | - CNAME: _YOUR_GITHUB_ACCOUNT_.github.io 90 | eg., etblue.github.io 91 | - Page Rule: http://etblue.github.io/+ forward http://etblue.github.io 92 | 93 | [Reference]( http://blog.cloudflare.com/introducing-pagerules-url-forwarding/) 94 | 95 | 96 | ### Step 4: Customize your Hackfoldr 97 | 98 | 1. Clone this [template sheet](https://ethercalc.org/hackfolder_template) into new ethercalc, and named whatever SLUG you want 99 | 100 | 2. Switch to `gh-pages` branch of your forked repo 101 | 102 | Thanks Github for their wonderful [Project Page](https://help.github.com/articles/user-organization-and-project-pages/#project-pages) feature, save us from renting own server for host hackfolder. 103 | 104 | 3. Edit `index.html` file and adjust the following settings 105 | 106 | - Change the linked ethercalc SLUG on [line 3](https://github.com/hackfoldr/hackfoldr-2.0-forkme/blob/gh-pages/index.html#L3) 107 | 108 | - Adjsut the drop-down menu as you wish in `