├── README.md ├── css └── parse-table.css ├── images ├── del.png ├── sort_asc.png ├── sort_both.png ├── sort_desc.png └── spinny.gif ├── index.html ├── js └── ParseTable.js └── license.txt /README.md: -------------------------------------------------------------------------------- 1 | Parse.com Ajax Datatable 2 | ======================== 3 | 4 | This is a simple flexible library that allows you to view and edit your parse.com 5 | data in a tabular format. You can set the table to be editable and/or deletable. 6 | For basic types the editing is very simple, you just click on the field and a little 7 | editor appears. For complex types and for large amounts of data in a single cell 8 | there are interceptor functions that allow you to handle the display of the data and 9 | the method for updating, e.g. for Date types you might intercept the up event and 10 | provide your favorite Datepicker. Here is a quick run down of the features: 11 | 12 | * pagable and sortable tabular interface 13 | * interceptor design pattern for adding custom display and update features 14 | * only 200 lines of code 15 | * relies on jquery, underscore, underscore templates and of course parse 16 | 17 | 18 | ![table](http://www.brenthamby.com/dt.png) 19 | 20 | This is the basic usage: 21 |
22 | 	pt = new ParseTable({
23 | 		el : $("#table"), // where to place on the page (fills space)
24 | 		parseObjName : "NAME_OF_TABLE",
25 | 		deletable : true, //  defaults to false (allows deleting if true)
26 | 		editable : true,  //  defaults to false (allows editing if true)
27 | 	...
28 | 
29 | 30 | By default it will inspect the table and present all of the columns for display and editing. You can enforce order or omit columns using: 31 | 32 |
33 | 	...
34 | 	cols : ['Street','City','State','PostalCode','Country'],
35 | 
36 | 37 | What is really powerful is the interceptors which allow you to customize the way the data is displayed and the method and UI for editing the data. For instance you may want to shorten long text entries so as not to distort the table: 38 | 39 |
40 | 	...
41 | 	displayInterceptors:{
42 | 		SOME_LONG_COLUMN : function(d){
43 | 			return d.substring(0,20)+"..."
44 | 		}
45 | 	},
46 |  
47 | 
48 | 49 | Or present a drop down list for some other enumerated column: 50 | 51 |
52 | 	...
53 | 	editInterceptors:{
54 | 		State : function(obj){
55 | 			var target = obj.el;  // DOM ref to the cell
56 | 			var parseObj = obj.parseObj; // parse instance
57 | 			var col = obj.col;  // column name in question
58 | 			var val = target.data("rawdata"); // raw data (diff if was displayIntercepted)
59 | 			var myEdit = $("<select class='pt_edit'><option>CA<option>NY<option>VT</select>")
60 | 				.val(target.html().trim()).change(function(){
61 | 				parseObj.set(obj.col,$(this).val())
62 | 				parseObj.save()
63 | 				pt.editing=false	
64 | 				pt.render()
65 | 			})
66 | 			target.append(myEdit)
67 | 		}
68 | 	}
69 | 
70 | 
71 | 72 | ### Authors and Contributors 73 | @brenthamby 74 | ### Support or Contact 75 | Having trouble with Pages? Check out the documentation at http://help.github.com/pages or contact support@github.com and we’ll help you sort it out. -------------------------------------------------------------------------------- /css/parse-table.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | *{ 4 | font-size:12px; 5 | font-family: arial; 6 | margin: 0; 7 | padding:0; 8 | text-shadow:1px 1px 1px #fff; 9 | color:#333; 10 | } 11 | p{ 12 | padding:20px; 13 | } 14 | ul{ 15 | padding:0px 40px 0 80px; 16 | } 17 | p, ul li{ 18 | font-size:17px; 19 | } 20 | h1{ 21 | font-size:23px; 22 | padding:20px; 23 | } 24 | body{ 25 | margin:20px 150px 0 150px; 26 | } 27 | .pt_container{ 28 | position: relative; 29 | min-width:100px; 30 | padding:14px; 31 | border:1px solid #ddd; 32 | } 33 | .pt_container .pt_controls{ 34 | position: relative; 35 | float: right; 36 | clear: both; 37 | margin:4px; 38 | } 39 | .pt_container .odd { 40 | background: #FAFAFB; 41 | } 42 | .pt_container .even{ 43 | background: #eeeeee; 44 | } 45 | .pt_container .pt_header{ 46 | background-image:url('../images/sort_both.png'); 47 | background-repeat:no-repeat; 48 | background-position:center right; 49 | } 50 | .pt_container .pt_ascending { 51 | background-image:url('../images/sort_asc.png'); 52 | background-repeat:no-repeat; 53 | background-position:center right; 54 | } 55 | .pt_container .pt_descending { 56 | background-image:url('../images/sort_desc.png'); 57 | background-repeat:no-repeat; 58 | background-position:center right; 59 | } 60 | 61 | .pt_container .top_row { 62 | 63 | background-image: linear-gradient(bottom, rgb(221,221,221) 25%, rgb(187,187,187) 50%, rgb(221,221,221) 75%); 64 | background-image: -o-linear-gradient(bottom, rgb(221,221,221) 25%, rgb(187,187,187) 50%, rgb(221,221,221) 75%); 65 | background-image: -moz-linear-gradient(bottom, rgb(221,221,221) 25%, rgb(187,187,187) 50%, rgb(221,221,221) 75%); 66 | background-image: -webkit-linear-gradient(bottom, rgb(221,221,221) 25%, rgb(187,187,187) 50%, rgb(221,221,221) 75%); 67 | background-image: -ms-linear-gradient(bottom, rgb(221,221,221) 25%, rgb(187,187,187) 50%, rgb(221,221,221) 75%); 68 | background-image: -webkit-gradient( 69 | linear, 70 | left bottom, 71 | left top, 72 | color-stop(0.25, rgb(221,221,221)), 73 | color-stop(0.5, rgb(187,187,187)), 74 | color-stop(0.75, rgb(221,221,221)) 75 | ); 76 | } 77 | 78 | .pt_container button{ 79 | padding:4px; 80 | font-size: 10px; 81 | font-weight:bold; 82 | } 83 | .pt_container table{ 84 | border-spacing: 0; 85 | border:2px solid #aaa; 86 | } 87 | .pt_container th, td{ 88 | border:0; 89 | border-right:2px solid #aaa; 90 | margin: 0; 91 | padding:8px; 92 | text-align: left; 93 | position: relative; 94 | } 95 | .pt_cell{ 96 | position: relative; 97 | display: block; 98 | } 99 | .pt_container table{ 100 | width:100%; 101 | } 102 | .pt_edit{ 103 | position:absolute; 104 | z-index:2; 105 | display:block; 106 | top:-5px; 107 | left:-5px; 108 | min-width: 200px; 109 | border:3px solid #bbb; 110 | background: #ddd; 111 | padding:4px; 112 | box-shadow: 5px 5px 5px #444; 113 | } 114 | .pt_edit textarea{ 115 | width:100%; 116 | } -------------------------------------------------------------------------------- /images/del.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brenthamby/parse-javascript-datatable/a9391e78e4fcc23ce07c4eb1ab8f28e7dd41b7e9/images/del.png -------------------------------------------------------------------------------- /images/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brenthamby/parse-javascript-datatable/a9391e78e4fcc23ce07c4eb1ab8f28e7dd41b7e9/images/sort_asc.png -------------------------------------------------------------------------------- /images/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brenthamby/parse-javascript-datatable/a9391e78e4fcc23ce07c4eb1ab8f28e7dd41b7e9/images/sort_both.png -------------------------------------------------------------------------------- /images/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brenthamby/parse-javascript-datatable/a9391e78e4fcc23ce07c4eb1ab8f28e7dd41b7e9/images/sort_desc.png -------------------------------------------------------------------------------- /images/spinny.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brenthamby/parse-javascript-datatable/a9391e78e4fcc23ce07c4eb1ab8f28e7dd41b7e9/images/spinny.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 67 | 68 | 69 | 70 | 71 |

Parse Data Table

72 | 73 |
74 |

75 | This is a simple flexible library that allows you to view and edit your parse.com 76 | data in a tabular format. You can set the table to be editable and/or deletable. 77 | For basic types the editing is very simple, you just click on the field and a little 78 | editor appears. For complex types and for large amounts of data in a single cell 79 | there are interceptor functions that allow you to handle the display of the data and 80 | the method for updating, e.g. for Date types you might intercept the up event and 81 | provide your favorite Datepicker. Here is a quick run down of the features: 82 |

88 | 89 |

90 |
91 | 92 | 95 | 117 | 118 | 121 | 138 | 139 | 142 | 162 | 165 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /js/ParseTable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ParseTable 3 | * @author brenthamby 4 | */ 5 | 6 | function ParseTable(opts){ 7 | if(!opts.parseObjName || !opts.el){ 8 | throw "ParseTable : provide parseObjName and el (where to put the thing)" 9 | } 10 | this.opts = { 11 | parseObjName : undefined, // name of Parse Class 12 | el : undefined, // element within which to place the table 13 | cols:[], // if not supplied will just derive order and names from server 14 | deletable : false, // if true, delete button will appear 15 | editable : false, // if true, inline editing will appear 16 | displayInterceptors:{}, 17 | editInterceptors:{} 18 | } 19 | 20 | _.extend(this.opts,opts) 21 | _.templateSettings.variable = "rc"; 22 | 23 | this.DOM; // DOM container 24 | this.id = Math.round(Math.random()*100000000); 25 | this.page=1; 26 | this.pageSize=1; 27 | this.sortBy; 28 | this.loading=false; 29 | this.editing=false; 30 | this.results; // parse results 31 | this.render(); 32 | } 33 | 34 | ParseTable.prototype={ 35 | spin:function(bool){ 36 | this.loading=bool 37 | if(bool) 38 | $("#pt_controls_loading"+this.id).show() 39 | else 40 | $("#pt_controls_loading"+this.id).fadeOut() 41 | }, 42 | next:function(e){ 43 | if(this.loading) return 44 | this.page++; 45 | this.render() 46 | }, 47 | prev:function(e){ 48 | if(this.loading) return 49 | this.page--; 50 | this.render() 51 | }, 52 | sort:function(e){ 53 | if(this.loading) return 54 | 55 | // GO BACK TO PAGE 1, DOES NOT MAKE SENSE TO ORDER IN MIDDLE 56 | 57 | this.page=1; 58 | this.sortBy=$(e.target).data("colname") 59 | var order = $(e.target).data("sortorder") 60 | this.sortOrder = (order === 'ascending') ? 'descending' : 'ascending' 61 | $("th").removeClass('pt_ascending').removeClass('pt_descending') 62 | $(e.target).addClass('pt_'+this.sortOrder) 63 | $(e.target).data("sortorder",this.sortOrder) 64 | this.render() 65 | }, 66 | update:function(e){ 67 | e.stopPropagation() 68 | this.editing=false 69 | if($(e.target).hasClass("js-cancel")){ 70 | this.DOM.find(".pt_edit").remove() 71 | } else { 72 | var text = $(e.target).parent().find("textarea") 73 | var parseId = $(e.target).closest("tr").attr("id") 74 | var col = $(e.target).parent().parent().data("colname") // ugg 75 | this.results[parseId].set(col, text.val()); 76 | this.results[parseId].save(null,{ 77 | success:function(){ 78 | this.render() 79 | }.bind(this), 80 | error:function(obj){ 81 | alert(obj.message) 82 | this.render() 83 | }.bind(this), 84 | }) 85 | } 86 | }, 87 | edit:function(e){ 88 | e.preventDefault() 89 | if(this.loading || this.editing) return 90 | this.editing=true 91 | var target = $(e.target); 92 | var col = $(e.target).data("colname") 93 | if(this.opts.editInterceptors[col]){ 94 | var parseId = $(e.target).closest("tr").attr("id") 95 | this.opts.editInterceptors[col]({ 96 | parseObj: this.results[parseId], 97 | col: col, 98 | el : target 99 | }); 100 | this.editing=false; 101 | return; 102 | } 103 | var editor = _.template( 104 | $( "script.pt_table_cell_edit" ).html() 105 | ); 106 | var val = target.data("rawdata") 107 | target.append(editor({value:val})) 108 | }, 109 | size:function(e){ 110 | this.pageSize = ($(e.target).val()) 111 | this.page=1 ; // FORCE TO BEGINNING IF RESIZING 112 | this.render() 113 | }, 114 | del:function(e){ 115 | if(this.loading) return 116 | this.spin(true); 117 | var id = $(e.target).closest("tr").attr("id") 118 | this.results[id].destroy({ 119 | success:function(){ 120 | this.render() 121 | }.bind(this), 122 | error:function(obj){ 123 | throw obj 124 | this.spin(false) 125 | }.bind(this) 126 | }) 127 | }, 128 | init:function(){ 129 | 130 | 131 | var container = _.template( 132 | $( "script.pt_table_container" ).html() 133 | ); 134 | 135 | // SET UP DOM AND DELEGATES 136 | 137 | this.DOM=$(container({id:this.id, deletable : this.opts.deletable })) 138 | this.opts.el.append(this.DOM) 139 | $(this.DOM).on("change","#pt_controls_size"+this.id,this.size.bind(this)) 140 | $(this.DOM).on("click","#pt_controls_next"+this.id,this.next.bind(this)) 141 | $(this.DOM).on("click","#pt_controls_prev"+this.id,this.prev.bind(this)) 142 | $(this.DOM).on("click",".pt_edit button",this.update.bind(this)) 143 | $(this.DOM).on("click","th",this.sort.bind(this)) 144 | if(this.opts.editable) 145 | $(this.DOM).on("click","div.js-editable",this.edit.bind(this)) 146 | if(this.opts.deletable) 147 | $(this.DOM).on("click","td.js-deletable",this.del.bind(this)) 148 | 149 | this.pageSize=$("#pt_controls_size"+this.id).val(); 150 | this.pObject=Parse.Object.extend(this.opts.parseObjName); 151 | this.query = new Parse.Query(this.pObject); 152 | 153 | }, 154 | buildHeaders:function(rows){ 155 | var headers = _.template( 156 | $( "script.pt_table_data_headers" ).html() 157 | ); 158 | $("#pt_table"+this.id).html( 159 | headers({ 160 | cols:this.opts.cols, 161 | deletable:this.opts.deletable 162 | })) 163 | this.headers=true; 164 | }, 165 | render: function(){ 166 | 167 | this.spin(true); 168 | if(!this.DOM){ 169 | this.init(); // ONE TIME CALL 170 | } 171 | 172 | this.query.limit(this.pageSize) 173 | 174 | if(this.page>1) 175 | this.query.skip((this.pageSize*this.page)-this.pageSize) 176 | else 177 | this.query.skip(0) // unsetting skip 178 | 179 | if(this.sortBy) 180 | if(this.sortOrder==='ascending') 181 | this.query.ascending(this.sortBy) 182 | else 183 | this.query.descending(this.sortBy) 184 | 185 | this.query.find({ 186 | 187 | success: function(results) { 188 | 189 | if(!results.length){ 190 | if(this.page>1) this.page--; 191 | this.spin(false); 192 | return; 193 | } 194 | 195 | if(results.length1) 196 | $("#pt_controls_next"+this.id).hide() 197 | else 198 | $("#pt_controls_next"+this.id).show() 199 | 200 | this.currLen=results.length; 201 | 202 | this.results={}; // STORE THE RESULTS INDEXED BY ID 203 | 204 | var rows =[]; 205 | 206 | if(!this.opts.cols.length) { // IF NOT SET GO WITH ORDER FROM FIRST PASS 207 | for(var n in results[0].attributes) 208 | this.opts.cols.push(n) 209 | } 210 | if(!this.headers) 211 | this.buildHeaders(this.opts.cols); 212 | 213 | for(var i = 0; i