├── .gitignore ├── .htaccess ├── LICENSE.txt ├── README.md ├── app ├── config │ ├── default.globals.ini │ ├── maps.ini │ └── routes.ini ├── models │ ├── board.php │ └── task.php └── views │ ├── about.html │ ├── home.html │ └── main.html ├── composer.json ├── css ├── bootstrap.min.css └── bootstrap.tweaks.css ├── favicon.ico ├── img ├── addeditems.png ├── additems.png ├── decentlist.png ├── demoshot.png ├── editlistname.png ├── glyphicons-halflings-white.png ├── glyphicons-halflings.png ├── lastlist.png ├── menu.png ├── newboard.png ├── newlist.png └── newlistname.png ├── index.php ├── js ├── bootstrap.min.js ├── jquery-2.0.3.min.js └── main.js ├── taskshare.sublime-project └── taskshare_db.sql /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.sublime-workspace 3 | *.sublime-project 4 | globals.ini 5 | vendor/ 6 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | # Enable rewrite engine and route requests to framework 2 | RewriteEngine On 3 | 4 | RewriteCond %{REQUEST_URI} \.ini$ 5 | RewriteRule \.ini$ - [R=404] 6 | 7 | RewriteCond %{REQUEST_URI} \.html?$ 8 | RewriteRule \.html?$ - [R=404] 9 | 10 | RewriteCond %{REQUEST_FILENAME} !-l 11 | RewriteCond %{REQUEST_FILENAME} !-f 12 | RewriteCond %{REQUEST_FILENAME} !-d 13 | RewriteRule .* index.php [L,QSA,E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Tim Habersack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TaskShare 2 | 3 | TaskShare is a really simple todo/task/list web app. No logins required. 4 | 5 | To see it in action: http://taskshare.org 6 | 7 | ## License 8 | 9 | Copyright 2014 Tim Habersack 10 | 11 | TaskShare is released under an MIT license. http://opensource.org/licenses/MIT 12 | -------------------------------------------------------------------------------- /app/config/default.globals.ini: -------------------------------------------------------------------------------- 1 | [globals] 2 | DEBUG=3 3 | UI=app/views/ 4 | LOGS=app/logs/ 5 | AUTOLOAD=app/models/ 6 | 7 | DBHOST=db_host_name_here 8 | DBNAME=database_name_here 9 | DBUSER=database_user_here 10 | DBPASS=database_pass_here 11 | DBPORT=3306 12 | -------------------------------------------------------------------------------- /app/config/maps.ini: -------------------------------------------------------------------------------- 1 | [maps] -------------------------------------------------------------------------------- /app/config/routes.ini: -------------------------------------------------------------------------------- 1 | [routes] 2 | -------------------------------------------------------------------------------- /app/models/board.php: -------------------------------------------------------------------------------- 1 | f3 = \Base::instance(); 10 | $this->db = $this->f3->get('db'); 11 | } 12 | function get() 13 | { 14 | $sql = "SELECT tasklists.id as listid, 15 | tasklists.listname, 16 | tasks.id as taskid, 17 | tasks.taskname 18 | FROM boards 19 | INNER JOIN tasklists ON tasklists.boardid=boards.id 20 | LEFT JOIN tasks ON tasks.tasklistid=tasklists.id 21 | WHERE boards.name=? 22 | ORDER BY listid ASC,taskid DESC"; 23 | $results = $this->db->exec($sql,$this->f3->get('PARAMS.boardcode')); 24 | 25 | $json_array = array(); 26 | 27 | if(count($results)>0) //is at least 1 list 28 | { 29 | $temp_array = array(); 30 | 31 | foreach($results as $result) 32 | { 33 | if(isset($temp_array['listid']) && $result['listid']==$temp_array['listid']) 34 | { 35 | // still in same list 36 | $supertemp_array = array(); 37 | $supertemp_array['taskid'] = $result['taskid']; 38 | $supertemp_array['taskname'] = $result['taskname']; 39 | array_push($temp_array['listitem'], $supertemp_array); 40 | unset($supertemp_array); 41 | } 42 | else 43 | { 44 | // we've hit a new list! 45 | if(count($temp_array)>0) array_push($json_array, $temp_array); 46 | $temp_array = array(); 47 | $temp_array['listid'] = $result['listid']; 48 | $temp_array['listname'] = $result['listname']; 49 | $temp_array['listitem'] = array(); 50 | if(isset($result['taskid'])) 51 | { 52 | $supertemp_array = array(); 53 | $supertemp_array['taskid'] = $result['taskid']; 54 | $supertemp_array['taskname'] = $result['taskname']; 55 | array_push($temp_array['listitem'], $supertemp_array); 56 | unset($supertemp_array); 57 | } 58 | } 59 | } 60 | // push the last list onto array 61 | if(count($temp_array)>0) array_push($json_array, $temp_array); 62 | } 63 | 64 | if($this->f3->get('internal')===true) 65 | { 66 | $this->f3->set('internal',false); 67 | return $json_array; 68 | } 69 | else echo utf8_encode(json_encode($json_array)); 70 | 71 | } 72 | 73 | function new_board() 74 | { 75 | if(isset($_POST['board_name'])) $new_name = $_POST['board_name']; // for future, when users can create a specifically-named board 76 | else $new_name = bin2hex(openssl_random_pseudo_bytes(10)); 77 | 78 | $rows = $this->db->exec("SELECT id FROM boards WHERE name=?",$new_name); 79 | 80 | while (count($rows) != 0) // loops until it generates a name that isn't in use 81 | { 82 | $new_name = bin2hex(openssl_random_pseudo_bytes(10)); 83 | $rows = $this->db->exec("SELECT id FROM boards WHERE name=?",$new_name); 84 | } 85 | 86 | if($this->db->exec("INSERT INTO boards (name) VALUES (?)",$new_name)) 87 | { 88 | $this->f3->reroute('/b/'.$new_name); 89 | } 90 | else $this->f3->reroute('/error/1000'); //TODO make an error handler page. 91 | //var_dump($this->db); 92 | } 93 | 94 | function post() 95 | { 96 | $rows = $this->db->exec("SELECT id FROM boards WHERE name=?",$this->f3->get('PARAMS.boardcode')); 97 | 98 | $boardid=$rows[0]['id']; 99 | 100 | if(count($rows)==0) 101 | { 102 | // there wasn't a board 103 | // TODO error this 104 | } 105 | $rows = $this->db->exec("INSERT INTO tasklists (boardid, listname) VALUES (?,'New List')",$boardid); 106 | 107 | if($rows>0) 108 | { 109 | $id = $this->db->lastInsertId(); 110 | $json_data = array('id'=>$id); 111 | header("HTTP/1.0 200 OK"); 112 | echo utf8_encode(json_encode($json_data)); 113 | } 114 | 115 | } 116 | 117 | function put() 118 | { 119 | // First verify this tasklist matches the boardnumber 120 | 121 | $sql = "SELECT tasklists.id 122 | FROM tasklists 123 | INNER JOIN boards ON boards.id=tasklists.boardid 124 | WHERE tasklists.id=:id AND boards.name=:bname"; 125 | 126 | $params = array( 127 | ':id'=>$this->f3->get('PARAMS.tasklistid'), 128 | ':bname'=>$this->f3->get('PARAMS.boardcode')); 129 | $rows = $this->db->exec($sql,$params); 130 | 131 | if(count($rows)>0) 132 | { 133 | //Let's update the db! 134 | $json_data = json_decode($this->f3->get('BODY'),true); 135 | $sql = "UPDATE tasklists SET listname=:listname WHERE id=:id"; 136 | $params = array(':listname'=>$json_data['listname'],':id'=>$this->f3->get('PARAMS.tasklistid')); 137 | $rows = $this->db->exec($sql,$params); 138 | if($rows>0) header("HTTP/1.0 200 OK"); 139 | else header("HTTP/1.0 402 Request Failed"); 140 | } 141 | else 142 | { 143 | header("HTTP/1.0 400 Bad Request"); 144 | } 145 | } 146 | 147 | function delete() 148 | { 149 | $sql = "SELECT tasklists.id 150 | FROM tasklists 151 | INNER JOIN boards ON boards.id=tasklists.boardid 152 | WHERE tasklists.id=:id AND boards.name=:bname"; 153 | 154 | $params = array( 155 | ':id'=>$this->f3->get('PARAMS.tasklistid'), 156 | ':bname'=>$this->f3->get('PARAMS.boardcode')); 157 | $rows = $this->db->exec($sql,$params); 158 | 159 | if(count($rows)>0) 160 | { 161 | //Let's update the db! 162 | $json_data = json_decode($this->f3->get('BODY'),true); 163 | 164 | $this->db->begin(); 165 | 166 | $sql = "DELETE FROM tasks WHERE tasklistid=:id"; 167 | $params = array(':id'=>$this->f3->get('PARAMS.tasklistid')); 168 | $taskrows = $this->db->exec($sql,$params); 169 | 170 | $sql = "DELETE FROM tasklists WHERE id=:id"; 171 | $params = array(':id'=>$this->f3->get('PARAMS.tasklistid')); 172 | $rows = $this->db->exec($sql,$params); 173 | 174 | $this->db->commit(); 175 | 176 | 177 | if($rows>0) header("HTTP/1.0 200 OK"); 178 | else header("HTTP/1.0 402 Request Failed"); 179 | } 180 | else 181 | { 182 | header("HTTP/1.0 400 Bad Request"); 183 | } 184 | } 185 | 186 | function loadboard() 187 | { 188 | $this->f3->set('internal',true); 189 | $this->f3->set('task_array',$this->get()); 190 | $this->f3->set('board_code',$this->f3->get('PARAMS.boardcode')); 191 | //print_r($board_array); 192 | echo View::instance()->render('main.html'); 193 | } 194 | } 195 | 196 | ?> -------------------------------------------------------------------------------- /app/models/task.php: -------------------------------------------------------------------------------- 1 | f3 = \Base::instance(); 10 | $this->db = $this->f3->get('db'); 11 | } 12 | function get() 13 | {} 14 | function post() 15 | { 16 | // First verify this tasklist matches the boardnumber 17 | 18 | $sql = "SELECT tasklists.id 19 | FROM tasklists 20 | INNER JOIN boards ON boards.id=tasklists.boardid 21 | WHERE tasklists.id=:id AND boards.name=:bname"; 22 | 23 | $params = array( 24 | ':id'=>$this->f3->get('PARAMS.tasklistid'), 25 | ':bname'=>$this->f3->get('PARAMS.boardcode')); 26 | $rows = $this->db->exec($sql,$params); 27 | 28 | if(count($rows)>0) 29 | { 30 | //Let's update the db! 31 | $json_data = json_decode($this->f3->get('BODY'),true); 32 | $sql = "INSERT INTO tasks (tasklistid,taskname) VALUES (:id,:taskname)"; 33 | $params = array(':taskname'=>$json_data['taskname'],':id'=>$this->f3->get('PARAMS.tasklistid')); 34 | $rows = $this->db->exec($sql,$params); 35 | if($rows>0) 36 | { 37 | $id = $this->db->lastInsertId(); 38 | $json_data = array('id'=>$id); 39 | header("HTTP/1.0 200 OK"); 40 | echo utf8_encode(json_encode($json_data)); 41 | } 42 | else header("HTTP/1.0 402 Request Failed"); 43 | } 44 | else 45 | { 46 | header("HTTP/1.0 400 Bad Request"); 47 | } 48 | } 49 | function put() 50 | { 51 | // First verify this tasklist matches the boardnumber 52 | 53 | $sql = "SELECT tasklists.id 54 | FROM tasklists 55 | INNER JOIN boards ON boards.id=tasklists.boardid 56 | WHERE tasklists.id=:id AND boards.name=:bname"; 57 | 58 | $params = array( 59 | ':id'=>$this->f3->get('PARAMS.tasklistid'), 60 | ':bname'=>$this->f3->get('PARAMS.boardcode')); 61 | $rows = $this->db->exec($sql,$params); 62 | 63 | if(count($rows)>0) 64 | { 65 | //Let's update the db! 66 | $json_data = json_decode($this->f3->get('BODY'),true); 67 | $sql = "UPDATE tasklists SET listname=:listname WHERE id=:id"; 68 | $params = array(':listname'=>$json_data['listname'],':id'=>$this->f3->get('PARAMS.tasklistid')); 69 | $rows = $this->db->exec($sql,$params); 70 | if($rows>0) header("HTTP/1.0 200 OK"); 71 | else header("HTTP/1.0 402 Request Failed"); 72 | } 73 | else 74 | { 75 | header("HTTP/1.0 400 Bad Request"); 76 | } 77 | 78 | 79 | //echo $this->f3->get('PARAMS.boardcode'); 80 | } 81 | function delete() 82 | { 83 | // First verify this tasklist matches the boardnumber 84 | 85 | $sql = "SELECT tasklists.id 86 | FROM tasklists 87 | INNER JOIN boards ON boards.id=tasklists.boardid 88 | WHERE tasklists.id=:id AND boards.name=:bname"; 89 | 90 | $params = array( 91 | ':id'=>$this->f3->get('PARAMS.tasklistid'), 92 | ':bname'=>$this->f3->get('PARAMS.boardcode')); 93 | $rows = $this->db->exec($sql,$params); 94 | 95 | if(count($rows)>0) 96 | { 97 | //Let's update the db! 98 | $json_data = json_decode($this->f3->get('BODY'),true); 99 | $sql = "DELETE FROM tasks WHERE id=:id AND tasklistid=:tasklistid LIMIT 1"; 100 | $params = array(':id'=>$json_data['taskid'],':tasklistid'=>$this->f3->get('PARAMS.tasklistid')); 101 | $rows = $this->db->exec($sql,$params); 102 | if($rows>0) header("HTTP/1.0 200 OK"); 103 | else header("HTTP/1.0 402 Request Failed"); 104 | } 105 | else 106 | { 107 | header("HTTP/1.0 400 Bad Request"); 108 | } 109 | } 110 | } 111 | 112 | ?> -------------------------------------------------------------------------------- /app/views/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TaskShare 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 51 |
52 |
53 |

About the project

54 |

TaskShare aims to help people manage and share simple task lists easily.

55 |

Honestly, my partner and I were in dire need of a better way to manage our lists. We were always remembering those one or two items we needed from this or that store. However, by the time we got there, either we had forgotten, or the list was with the other person.. it wasn't good.

56 |

We've been using TaskShare for about 8 months, and it is a part of our life/flow now. I hope others find it useful.

57 |

Getting Started

58 |

Briefly..

59 |

Create a new taskboard by using the ginormous button on the home page. New lists are added by the New Tasklist button on the top left to create your first list. Clicking on the list title (default is New List) brings up a small nav.

60 |
    61 |
  • Add task(s) to list
  • 62 |
  • Edit list name
  • 63 |
  • Delete list
  • 64 |
65 |

Share your list by sending them the pages address. Done.

66 |

One minute tutorial

67 |

Okay, first thing we need is a new Taskboard. Click on the Create TaskBoard Now button on the homepage. It should take you to your new Taskboard! How simple was that?

68 | 69 |

Looking good! Hm, but we need a place to put our todo list.. Click on the New Tasklist button on the top left to create your first list.

70 | 71 |

Okay, so that is something. But, let's not keep the name New List, Let's pretend we are going to share your weekly grocery list with your partner. Let's make it a little more clear. Click on the list title (New List) and a menu appears

72 | 73 |

Options! Okay, we want to rename this, so let's hit the pencil.

74 | 75 |

Type in 'Groceries' and hit enter or click on the Done button.

76 | 77 |

All right! We need to actually add some items to our grocery list, so click the list title (Groceries) again to bring up the menu. This time, hit the plus icon.

78 | 79 |

Just start adding items into the list, hitting enter or Add button. to add that item to your list.

80 | 81 |

When you've added all the items you want, click the Done Adding button to get rid of that input area.

82 | 83 |

Feel free to have as many lists as you want. Share your lists by giving someone the url of the page; it's that simple.

84 | 85 | 86 | 87 | 88 | 89 | 90 |

Frequently Asked Questions

91 |
92 |
Wait, no login?? How is my list secure then?!
93 |
Pump the breaks. This is for non-secure lists. If someone guesses your uri, then they can see your taskboard. I've taken measures to minimize brute-forcing being successful, and if it becomes an issue I'll probably make the unique taskboard name longer.
94 |
I don't trust you with my list
95 |
Gosh, why so negative?? Okay then, well it's open-source. Go to our repo on github and set it up on your own server.
96 |
97 |

Credits

98 | 102 |

License

103 |

Copyright 2014 Tim Habersack

104 |

TaskShare is released under an MIT license. http://opensource.org/licenses/MIT

105 |

Author

106 | 109 | 110 |
111 |
112 | 113 | 114 | 115 | 116 |
117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /app/views/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TaskShare 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 54 | 55 | 56 | 57 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 |
69 | 73 |

TaskShare

74 |
75 | 76 |
77 | 78 |
79 |

Your TaskShare Instance

80 | Create Taskboard Now 81 |
82 | 83 |
84 | 85 | 89 | 90 |
91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /app/views/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TaskShare 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 47 | 48 |
49 |
50 |
    51 | 52 |
  • 53 |
    54 |

    55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
    64 |
    65 |
  • 66 | 67 |
68 | 69 |
70 | 71 |
72 | 73 |
74 | 75 | 76 | 77 |
78 |
79 | 80 |
81 | 82 |
83 | 84 | 85 |
86 |
87 | 88 |
89 |
90 | 91 |
92 | 93 | 94 | 95 |
96 |
97 | 98 | 99 |
100 | 101 | 102 | 103 |
104 | 105 |
106 | 107 | 108 | 109 | 110 |
111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "bcosca/fatfree": ">=3.2" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /css/bootstrap.tweaks.css: -------------------------------------------------------------------------------- 1 | .taskname, .list_title 2 | { 3 | cursor:pointer; 4 | } 5 | 6 | .check_td 7 | { 8 | width:40px; 9 | } 10 | 11 | #templates 12 | { 13 | display: none; 14 | } 15 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/favicon.ico -------------------------------------------------------------------------------- /img/addeditems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/addeditems.png -------------------------------------------------------------------------------- /img/additems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/additems.png -------------------------------------------------------------------------------- /img/decentlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/decentlist.png -------------------------------------------------------------------------------- /img/demoshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/demoshot.png -------------------------------------------------------------------------------- /img/editlistname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/editlistname.png -------------------------------------------------------------------------------- /img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /img/lastlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/lastlist.png -------------------------------------------------------------------------------- /img/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/menu.png -------------------------------------------------------------------------------- /img/newboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/newboard.png -------------------------------------------------------------------------------- /img/newlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/newlist.png -------------------------------------------------------------------------------- /img/newlistname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timbotron/taskshare/9503d49cf77b8529c6cd0909d62f80cfe3654eac/img/newlistname.png -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | config('app/config/globals.ini'); 6 | $f3->config('app/config/routes.ini'); 7 | $f3->config('app/config/maps.ini'); 8 | 9 | $f3->set('db',new \DB\SQL('mysql:host='.$f3->get('DBHOST').';port='.$f3->get('DBPORT').';dbname='.$f3->get('DBNAME'),$f3->get('DBUSER'),$f3->get('DBPASS'))); 10 | 11 | $f3->route('GET /', 12 | function() { 13 | echo View::instance()->render('home.html'); 14 | } 15 | ); 16 | 17 | $f3->route('GET /about', 18 | function() { 19 | echo View::instance()->render('about.html'); 20 | } 21 | ); 22 | 23 | $f3->route('GET /test', 24 | function() { 25 | echo View::instance()->render('test.html'); 26 | } 27 | ); 28 | 29 | $f3->route('GET /new','Board->new_board'); 30 | 31 | $f3->map('/tasklist/@tasklistid/@boardcode','Board'); 32 | $f3->map('/task/@tasklistid/@boardcode','Task'); 33 | 34 | $f3->route('GET /b/@boardcode','Board->loadboard'); 35 | 36 | $f3->route('GET /minify/@type', 37 | function($f3,$args) { 38 | $f3->set('UI',$args['type'].'/'); 39 | echo Web::instance()->minify($_GET['files']); 40 | }, 41 | 3600 42 | ); 43 | $f3->run(); 44 | -------------------------------------------------------------------------------- /js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('