├── .DS_Store ├── README.md ├── screenshots ├── LAB-Javascript-Playground.png └── LAB-codefriar-ngforce.png └── src ├── .DS_Store ├── applications └── LABJS_Javascript_Playground.app ├── classes ├── LABJS_CodeFriar_TestUtils.cls ├── LABJS_CodeFriar_TestUtils.cls-meta.xml ├── LABJS_CodeFriar_ngForceController_TEST.cls ├── LABJS_CodeFriar_ngForceController_TEST.cls-meta.xml ├── LABJS_SimpleController.cls ├── LABJS_SimpleController.cls-meta.xml ├── LABJS_SimpleControllerTest.cls ├── LABJS_SimpleControllerTest.cls-meta.xml ├── RemoteTKController.cls ├── RemoteTKController.cls-meta.xml ├── TestRemoteTKController.cls ├── TestRemoteTKController.cls-meta.xml ├── ngForceController.cls └── ngForceController.cls-meta.xml ├── components ├── LABJS_RemoteTK.component ├── LABJS_RemoteTK.component-meta.xml ├── LABJS_ngforce.component └── LABJS_ngforce.component-meta.xml ├── documents ├── LAB_JAVASCRIPT_PLAYGROUND_FOLDER-meta.xml └── LAB_JAVASCRIPT_PLAYGROUND_FOLDER │ ├── LAB_JAVASCRIPT_PLAYGROUND.png │ └── LAB_JAVASCRIPT_PLAYGROUND.png-meta.xml ├── layouts └── LABJS_Mo_Tester_2__c-Mo Tester 2 Layout.layout ├── objects ├── Contact.object └── LABJS_Mo_Tester_2__c.object ├── package.xml ├── pages ├── LABJS_Angular105.page ├── LABJS_Angular105.page-meta.xml ├── LABJS_Angular105_ContactList.page ├── LABJS_Angular105_ContactList.page-meta.xml ├── LABJS_Angular105_ContactList_ForceTK.page ├── LABJS_Angular105_ContactList_ForceTK.page-meta.xml ├── LABJS_Angular105_Hello.page ├── LABJS_Angular105_Hello.page-meta.xml ├── LABJS_Backbone10.page ├── LABJS_Backbone10.page-meta.xml ├── LABJS_Backbone10_Todos.page ├── LABJS_Backbone10_Todos.page-meta.xml ├── LABJS_Bootstrap231.page ├── LABJS_Bootstrap231.page-meta.xml ├── LABJS_Bootstrap231_Hello.page ├── LABJS_Bootstrap231_Hello.page-meta.xml ├── LABJS_ForceTK.page ├── LABJS_ForceTK.page-meta.xml ├── LABJS_ForceTK_AccountList.page ├── LABJS_ForceTK_AccountList.page-meta.xml ├── LABJS_JQuery191.page ├── LABJS_JQuery191.page-meta.xml ├── LABJS_JQuery191_DragDrop.page ├── LABJS_JQuery191_DragDrop.page-meta.xml ├── LABJS_JQuery191_Simple.page ├── LABJS_JQuery191_Simple.page-meta.xml ├── LABJS_JQuery191_Sortable.page ├── LABJS_JQuery191_Sortable.page-meta.xml ├── LABJS_JQueryMobile130.page ├── LABJS_JQueryMobile130.page-meta.xml ├── LABJS_JQueryMobile130_Hello.page ├── LABJS_JQueryMobile130_Hello.page-meta.xml ├── LABJS_ngForceDemo.page ├── LABJS_ngForceDemo.page-meta.xml ├── LABJS_oppBoxTmpl.page ├── LABJS_oppBoxTmpl.page-meta.xml ├── LABJS_oppDetail.page ├── LABJS_oppDetail.page-meta.xml ├── LABJS_oppDetailsTmpl.page └── LABJS_oppDetailsTmpl.page-meta.xml ├── staticresources ├── LABJS_Angular.resource ├── LABJS_Angular.resource-meta.xml ├── LABJS_Backbone.resource ├── LABJS_Backbone.resource-meta.xml ├── LABJS_Bootstrap.resource ├── LABJS_Bootstrap.resource-meta.xml ├── LABJS_ForceTK.resource ├── LABJS_ForceTK.resource-meta.xml ├── LABJS_jQuery.resource ├── LABJS_jQuery.resource-meta.xml ├── LABJS_jQueryMobile.resource ├── LABJS_jQueryMobile.resource-meta.xml ├── LABJS_ngForce.resource └── LABJS_ngForce.resource-meta.xml └── tabs ├── Backbone_1_0.tab ├── Bootstrap_2_3_1.tab ├── ForceTK.tab ├── JQuery_191_Examples.tab ├── JQuery_Mobile_130_Examples.tab └── LABJS_AngularJS.tab /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Welcome to Reid's Javascript Playground 2 | ======================================= 3 | 4 | I've been playing with Javascript, so I thought I'd put everything together in a single org so it's convenient for you to try as well. 5 | 6 | NOTE: this is for demo purposes, is offered as is without warranty. Do not install into a production org. (Seriously: go get a [free Dev Org](http://developer.force.com/join).) 7 | 8 | [Install into a dev org from this unmanaged package.](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tE0000000E9LB) 9 | 10 | If you have an example you'd like to see (or contribute), go for it. 11 | 12 | ![Screenshot](https://raw.github.com/ReidCarlberg/LAB-Javascript-Playground/master/screenshots/LAB-Javascript-Playground.png "Optional title") 13 | 14 | Great add from CodeFriar -- AngularJS / ngForce example. 15 | 16 | ![Screenshot](https://raw.github.com/ReidCarlberg/LAB-Javascript-Playground/master/screenshots/LAB-codefriar-ngforce.png "Optional title") 17 | -------------------------------------------------------------------------------- /screenshots/LAB-Javascript-Playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/screenshots/LAB-Javascript-Playground.png -------------------------------------------------------------------------------- /screenshots/LAB-codefriar-ngforce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/screenshots/LAB-codefriar-ngforce.png -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/.DS_Store -------------------------------------------------------------------------------- /src/applications/LABJS_Javascript_Playground.app: -------------------------------------------------------------------------------- 1 | 2 | 3 | standard-home 4 | 5 | LAB_JAVASCRIPT_PLAYGROUND_FOLDER/LAB_JAVASCRIPT_PLAYGROUND.png 6 | JQuery_191_Examples 7 | JQuery_Mobile_130_Examples 8 | Bootstrap_2_3_1 9 | Backbone_1_0 10 | LABJS_AngularJS 11 | ForceTK 12 | 13 | -------------------------------------------------------------------------------- /src/classes/LABJS_CodeFriar_TestUtils.cls: -------------------------------------------------------------------------------- 1 | public with sharing class LABJS_CodeFriar_TestUtils { 2 | 3 | public static Account getAccount() { 4 | Account a = new Account(); 5 | String unique = String.valueOf(DateTime.now().getTime()); 6 | a.name = 'foo company ' + unique; 7 | insert a; 8 | return a; 9 | } 10 | 11 | public static LABJS_Mo_Tester_2__c getMoTester2() { 12 | LABJS_Mo_Tester_2__c mo2 = new LABJS_Mo_Tester_2__c(); 13 | insert mo2; 14 | return mo2; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /src/classes/LABJS_CodeFriar_TestUtils.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LABJS_CodeFriar_ngForceController_TEST.cls: -------------------------------------------------------------------------------- 1 | @isTest 2 | public class LABJS_CodeFriar_ngForceController_TEST{ 3 | private static String tooLongAccName = 'LOTS OF '+ 4 | 'CHARACTERS XXXXXXXXXXXXXXXXXXXXXXXX'+ 5 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 6 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 7 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 8 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 9 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 10 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 11 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 12 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 13 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 14 | 'XXXXXXXXXXXXXXXX'; 15 | 16 | static private void assertError(String jsonResult, String expectedError, String method) { 17 | system.debug('##################' + jsonResult); 18 | List errorArray = (List)JSON.deserializeUntyped(jsonResult); 19 | 20 | System.assertNotEquals(null, errorArray, 21 | 'error array missing from '+method+' result'); 22 | System.assertNotEquals(0, errorArray.size(), 23 | 'error array is empty in '+method+' result'); 24 | 25 | Map error = (Map)errorArray[0]; 26 | String errorCode = (String)error.get('errorCode'); 27 | System.assertNotEquals(null, errorCode, 28 | 'errorCode property missing from '+method+' result'); 29 | System.assertEquals(expectedError, errorCode, 30 | 'errorCode should be '+expectedError+' in '+method+' result'); 31 | } 32 | 33 | static testMethod void testDescribe() { 34 | // Assume we have accounts 35 | String jsonResult = ngForceController.describe('Account'); 36 | 37 | System.assertNotEquals(null, jsonResult, 38 | 'ngForceController.describe returned null'); 39 | 40 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 41 | 42 | System.assertNotEquals(null, result.get('fields'), 43 | 'fields property missing from ngForceController.describe result'); 44 | 45 | // TODO - more assertions on describe results 46 | 47 | // Invalid object type 48 | // Hope there isn't a QXZXQZXZQXZQ object type! 49 | jsonResult = ngForceController.describe('QXZXQZXZQXZQ'); 50 | assertError(jsonResult, 'NOT_FOUND', 'ngForceController.describe'); 51 | } 52 | 53 | static private void assertRecord(Map record, String accName, String accNumber, String method) { 54 | Map attributes = (Map)record.get('attributes'); 55 | System.assertNotEquals(null, attributes, 56 | 'attributes property missing from '+method+' result'); 57 | System.assertNotEquals(0, attributes.keySet().size(), 58 | 'empty attributes object in '+method+' result'); 59 | 60 | String type = (String)attributes.get('type'); 61 | System.assertNotEquals(null, type, 62 | 'type property missing from '+method+' result'); 63 | System.assertEquals('Account', type, 64 | 'Wrong type in '+method+' result'); 65 | 66 | String url = (String)attributes.get('url'); 67 | System.assertNotEquals(null, url, 68 | 'url property missing from '+method+' result'); 69 | 70 | Id id = (Id)record.get('Id'); 71 | System.assertNotEquals(null, id, 72 | 'Id property missing from '+method+' result'); 73 | Account account = [SELECT Id, Name FROM Account WHERE Id = :id LIMIT 1]; 74 | System.assertNotEquals(null, account, 75 | 'Couldn\'t find account record identified by '+method+' result'); 76 | System.assertEquals(accName, account.Name, 77 | 'Account name doesn\'t match in '+method+' result'); 78 | 79 | String name = (String)record.get('Name'); 80 | System.assertNotEquals(null, name, 81 | 'Name property missing from '+method+' result'); 82 | System.assertEquals(accName, name, 83 | 'Wrong account name in '+method+' result'); 84 | 85 | String accountNumber = (String)record.get('AccountNumber'); 86 | System.assertNotEquals(null, name, 87 | 'AccountNumber property missing from '+method+' result'); 88 | System.assertEquals(accNumber, accountNumber, 89 | 'Wrong account number in '+method+' result'); 90 | } 91 | 92 | static private Id testCreate(String accName, String accNumber, String fields) { 93 | // Assume we can create an account 94 | 95 | // Try with data in correct types 96 | String jsonResult = ngForceController.create('Account', 97 | '{"Name": "'+accName+'", '+ 98 | '"AccountNumber" : "'+accNumber+'",'+ 99 | fields+'}'); 100 | 101 | System.assertNotEquals(null, jsonResult, 102 | 'ngForceController.create returned null'); 103 | 104 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 105 | 106 | Boolean success = (Boolean)result.get('success'); 107 | System.assertNotEquals(null, success, 108 | 'success property missing from ngForceController.create result'); 109 | System.assertNotEquals(false, success, 110 | 'success is false in ngForceController.create result'); 111 | 112 | List errors = (List)result.get('errors'); 113 | System.assertNotEquals(null, errors, 114 | 'errors property missing from ngForceController.create result'); 115 | System.assertEquals(0, errors.size(), 116 | 'errors array is not empty in ngForceController.create result'); 117 | 118 | Id id = (Id)result.get('id'); 119 | System.assertNotEquals(null, id, 120 | 'id property missing from ngForceController.create result'); 121 | Account account = [SELECT Id, Name, AccountNumber FROM Account LIMIT 1]; 122 | System.assertNotEquals(null, account, 123 | 'Couldn\'t find account record created by ngForceController.create result'); 124 | System.assertEquals(accName, account.Name, 125 | 'Account name doesn\'t match in ngForceController.create result'); 126 | System.assertEquals(accNumber, account.AccountNumber, 127 | 'Account number doesn\'t match in ngForceController.create result'); 128 | 129 | jsonResult = ngForceController.create('QXZXQZXZQXZQ', '{"Name": "'+accName+'"}'); 130 | assertError(jsonResult, 'NOT_FOUND', 'ngForceController.create'); 131 | 132 | jsonResult = ngForceController.create('Account', '{"Name" "'+accName+'"}'); 133 | assertError(jsonResult, 'JSON_PARSER_ERROR', 'ngForceController.create'); 134 | 135 | jsonResult = ngForceController.create('Account', '{"XQZXQZXQZXQZ" : "'+accName+'"}'); 136 | assertError(jsonResult, 'INVALID_FIELD', 'ngForceController.create'); 137 | 138 | jsonResult = ngForceController.create('Account', '{"Name" : "'+tooLongAccName+'"}'); 139 | assertError(jsonResult, 'STRING_TOO_LONG', 'ngForceController.create'); 140 | 141 | return id; 142 | } 143 | 144 | static private void testRetrieve(String accName, String accNumber, Id id) { 145 | String jsonResult = ngForceController.retrieve('Account', id, 'Name, AccountNumber'); 146 | 147 | System.assertNotEquals(null, jsonResult, 148 | 'ngForceController.retrieve returned null'); 149 | 150 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 151 | 152 | assertRecord(result, accName, accNumber, 'ngForceController.retrieve'); 153 | 154 | // TODO - test negative paths for retrieve 155 | } 156 | 157 | static private void testQuery(String accName, String accNumber) { 158 | String jsonResult = ngForceController.query('SELECT Id, Name, AccountNumber FROM Account WHERE Name = \''+accName+'\''); 159 | 160 | System.assertNotEquals(null, jsonResult, 161 | 'ngForceController.query returned null'); 162 | 163 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 164 | 165 | List records = (List)result.get('records'); 166 | System.assertNotEquals(null, records, 167 | 'records property missing from ngForceController.query result'); 168 | System.assertEquals(1, records.size(), 169 | 'records array should have single record in ngForceController.query result'); 170 | 171 | Map record = (Map)records[0]; 172 | 173 | assertRecord(record, accName, accNumber, 'ngForceController.query'); 174 | 175 | Integer totalSize = (Integer)result.get('totalSize'); 176 | System.assertNotEquals(null, totalSize, 177 | 'totalSize property missing from ngForceController.query result'); 178 | System.assertEquals(1, totalSize, 179 | 'totalSize should be 1 in ngForceController.query result'); 180 | 181 | Boolean done = (Boolean)result.get('done'); 182 | System.assertNotEquals(null, done, 183 | 'done property missing from ngForceController.query result'); 184 | System.assertEquals(true, done, 185 | 'done should be true in ngForceController.query result'); 186 | 187 | jsonResult = ngForceController.query('SSSSSS Id, Name FROM Account WHERE Name = \''+accName+'\''); 188 | assertError(jsonResult, 'INVALID_QUERY', 'ngForceController.query'); 189 | } 190 | 191 | static private void testSearch(String accName, String accNumber, Id id) { 192 | Id [] fixedSearchResults= new Id[1]; 193 | fixedSearchResults[0] = id; 194 | Test.setFixedSearchResults(fixedSearchResults); 195 | String jsonResult = ngForceController.search('FIND {'+accName+'} IN ALL FIELDS RETURNING Account (Id, Name, AccountNumber)'); 196 | 197 | System.assertNotEquals(null, jsonResult, 198 | 'ngForceController.search returned null'); 199 | 200 | List result = (List)JSON.deserializeUntyped(jsonResult); 201 | 202 | List records = (List)result[0]; 203 | 204 | Map record = (Map)records[0]; 205 | 206 | assertRecord(record, accName, accNumber, 'ngForceController.search'); 207 | 208 | jsonResult = ngForceController.search('FFFF {'+accName+'} IN ALL FIELDS RETURNING Account (Id, Name)'); 209 | assertError(jsonResult, 'INVALID_SEARCH', 'ngForceController.search'); 210 | } 211 | 212 | static private void testUpdate(String accName, String accNumber, Id id, String fields) { 213 | String jsonResult = ngForceController.updat('Account', id, '{"Name":"'+accName+'", "AccountNumber":"'+accNumber+'"}'); 214 | System.assertEquals(null, jsonResult, 215 | 'Non-null result from ngForceController.updat'); 216 | Account account = [SELECT Id, Name, AccountNumber FROM Account WHERE Id = :id LIMIT 1]; 217 | System.assertNotEquals(null, account, 218 | 'Couldn\'t find account record after ngForceController.updat'); 219 | System.assertEquals(accName, account.Name, 220 | 'Account name doesn\'t match after ngForceController.updat'); 221 | System.assertEquals(accNumber, account.AccountNumber, 222 | 'Account number doesn\'t match after ngForceController.updat'); 223 | 224 | jsonResult = ngForceController.updat('QXZXQZXZQXZQ', id, '{"Name":"'+accName+'"}'); 225 | assertError(jsonResult, 'NOT_FOUND', 'ngForceController.updat'); 226 | 227 | jsonResult = ngForceController.updat('Account', id, '{"XQZXQZXQZXQZ" : "'+accName+'"}'); 228 | assertError(jsonResult, 'INVALID_FIELD', 'ngForceController.updat'); 229 | 230 | } 231 | 232 | static private void testUpsert(String accName, String accNumber, String id, String fields) { 233 | String jsonResult = ngForceController.upser('Account', 234 | 'Id', 235 | (String)id, 236 | '{"Name":"'+accName+'", '+ 237 | '"AccountNumber":"'+accNumber+'",'+ 238 | fields+'}'); 239 | System.assertEquals(null, jsonResult, 240 | 'Non-null result from ngForceController.upser'); 241 | Account account = [SELECT Id, Name, AccountNumber FROM Account WHERE id = :id LIMIT 1]; 242 | System.assertNotEquals(null, account, 243 | 'Couldn\'t find account record after ngForceController.upser'); 244 | System.assertEquals(accName, account.Name, 245 | 'Account name doesn\'t match after ngForceController.upser'); 246 | System.assertEquals(accNumber, account.AccountNumber, 247 | 'Account number doesn\'t match after ngForceController.upser'); 248 | } 249 | 250 | static private void testDelete(Id id) { 251 | String jsonResult = ngForceController.del('QXZXQZXZQXZQ', id); 252 | assertError(jsonResult, 'NOT_FOUND', 'ngForceController.del'); 253 | 254 | jsonResult = ngForceController.del('Account', id); 255 | System.assertEquals(null, jsonResult, 256 | 'Non-null result from ngForceController.del'); 257 | List accounts = [SELECT Id, Name FROM Account WHERE Id = :id]; 258 | System.assertEquals(0, accounts.size(), 259 | 'Account record was not deleted by ngForceController.del'); 260 | 261 | jsonResult = ngForceController.del('Account', id); 262 | assertError(jsonResult, 'ENTITY_IS_DELETED', 'ngForceController.del'); 263 | } 264 | 265 | static testMethod void testGetObjType() { 266 | Account a = LABJS_CodeFriar_TestUtils.getAccount(); 267 | String jsonResult = ngForceController.getObjType(a.id); 268 | System.assertNotEquals(null, jsonResult, 269 | 'ngForceController.getObjType returned null'); 270 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 271 | System.assertEquals('Account', result.get('type')); 272 | } 273 | 274 | static testMethod void testDescribeFieldSet() { 275 | //warning this test is un-optimal and fragile 276 | //I cannot create a fieldset via apex, so this test relies 277 | //on a given object and a given fieldset existing!!! 278 | 279 | String jsonResult = ngForceController.describeFieldSet('LABJS_Mo_Tester_2__c', 'details'); 280 | System.assertNotEquals(null, jsonResult); 281 | } 282 | 283 | static testMethod void testSoqlFromFieldSet() { 284 | //warning this test is un-optimal and fragile 285 | //I cannot create a fieldset via apex, so this test relies 286 | //on a given object and a given fieldset existing!!! 287 | 288 | String jsonResult = ngForceController.soqlFromFieldSet('LABJS_Mo_Tester_2__c', 'details'); 289 | System.assertNotEquals(null, jsonResult); 290 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 291 | System.assertNotEquals(null, result.get('selectClause')); 292 | System.assertNotEquals(null, result.get('fromClause')); 293 | } 294 | 295 | static testMethod void testQueryFromFieldSet() { 296 | //warning this test is un-optimal and fragile 297 | //I cannot create a fieldset via apex, so this test relies 298 | //on a given object and a given fieldset existing!!! 299 | 300 | LABJS_Mo_Tester_2__c li = LABJS_CodeFriar_TestUtils.getMoTester2(); 301 | 302 | String jsonResult = ngForceController.queryFromFieldSet(li.Id, 'details'); 303 | System.assertNotEquals(null, jsonResult); 304 | List result = (List)JSON.deserializeUntyped(jsonResult); 305 | System.assertNotEquals(null, result); 306 | } 307 | 308 | static testMethod void testGetPicklistValues() { 309 | String jsonResult = ngForceController.getPicklistValues('Opportunity', 'StageName'); 310 | System.assertNotEquals(null, jsonResult); 311 | List result = (List)JSON.deserializeUntyped(jsonResult); 312 | System.assertNotEquals(null, result); 313 | } 314 | 315 | static testMethod void testBulkCreate(){ 316 | Map js = new Map(); 317 | String fields = '{"0":{"Name":"foo company 1368751413359"},"1":{"Name":"foo company 1368751413440"},"2":{"Name":"foo company 1368751413459"},"3":{"Name":"foo company 1368751413479"},"4":{"Name":"foo company 1368751413497"},"5":{"Name":"foo company 1368751413517"},"6":{"Name":"foo company 1368751413540"},"7":{"Name":"foo company 1368751413559"},"8":{"Name":"foo company 1368751413577"},"9":{"Name":"foo company 1368751413597"}}'; 318 | String jsonResult = ngForceController.bulkCreate('Account', fields); 319 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 320 | System.AssertNotEquals(result.get('id'), null); 321 | System.AssertEquals(result.get('success'), true); 322 | } 323 | 324 | static testMethod void testCRUD() { 325 | String accName = 'Test1'; 326 | String accNumber = '1234'; 327 | 328 | // String field values 329 | Id id = testCreate(accName, accNumber, '"AnnualRevenue" : "10000.00",'+ 330 | '"NumberOfEmployees" : "1000",'+ 331 | '"Phone" : "(111) 222-3333"'); 332 | testDelete(id); 333 | 334 | // Integer field values 335 | id = testCreate(accName, accNumber, '"AnnualRevenue" : 1000000,'+ 336 | '"NumberOfEmployees" : 1000,'+ 337 | '"Phone" : "(111) 222-3333"'); 338 | testRetrieve(accName, accNumber, id); 339 | testQuery(accName, accNumber); 340 | testSearch(accName, accNumber, id); 341 | testUpdate(accName+'1', accNumber+'1', id, '"AnnualRevenue" : "1100000",'+ 342 | '"NumberOfEmployees" : "1100",'+ 343 | '"Phone" : "(112) 222-3333"'); 344 | testUpdate(accName+'2', accNumber+'2', id, '"AnnualRevenue" : "2000000",'+ 345 | '"NumberOfEmployees" : "2000",'+ 346 | '"Phone" : "(222) 222-3333"'); 347 | testUpsert(accName+'3', accNumber+'3', id, '"AnnualRevenue" : 3000000,'+ 348 | '"NumberOfEmployees" : 3000,'+ 349 | '"Phone" : "(333) 222-3333"'); 350 | testUpsert(accName+'4', accNumber+'4', id, '"AnnualRevenue" : 4000000,'+ 351 | '"NumberOfEmployees" : 4000,'+ 352 | '"Phone" : "(444) 222-3333"'); 353 | testDelete(id); 354 | } 355 | } -------------------------------------------------------------------------------- /src/classes/LABJS_CodeFriar_ngForceController_TEST.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LABJS_SimpleController.cls: -------------------------------------------------------------------------------- 1 | public class LABJS_SimpleController{ 2 | 3 | public Id targetContactId { get; set; } 4 | public Contact targetContact { get; set; } 5 | public String targetContactLeadSource { get; set; } 6 | public String contactSortableIndexJsonString { get; set; } 7 | 8 | public List getContacts() { 9 | return [Select ID, Name, Phone, LeadSource, Sortable_Index__c From Contact Order By Sortable_Index__c LIMIT 100]; 10 | } 11 | 12 | public String getContactsJson() { 13 | List contacts = getContacts(); 14 | String ret = JSON.serialize(contacts); 15 | return ret; 16 | } 17 | 18 | public List getLeadSources() { 19 | List ret = new List(); 20 | for (Schema.PicklistEntry current : Contact.LeadSource.getDescribe().getPicklistValues()) { 21 | ret.add(current.getValue()); 22 | } 23 | return ret; 24 | } 25 | 26 | public void updateContactLeadSource() { 27 | System.debug('*****' + this.targetContactId); 28 | Contact c = new Contact(); 29 | c.id = this.targetContactId; 30 | c.leadsource = this.targetContactLeadSource; 31 | update c; 32 | } 33 | 34 | public void updateContactSortableIndex() { 35 | System.debug('**** updateContactSortableIndex ' + contactSortableIndexJsonString); 36 | 37 | String[] splitIds = contactSortableIndexJsonString.split('_'); 38 | 39 | List toUpdate = new List(); 40 | Integer index = 0; 41 | 42 | for (String current : splitIds) { 43 | index++; 44 | Contact updateContact = new Contact(); 45 | updateContact.Id = current; 46 | updateContact.Sortable_Index__c = index; 47 | toUpdate.add(updateContact); 48 | } 49 | 50 | update toUpdate; 51 | } 52 | 53 | public void updateTargetContact() { 54 | 55 | Contact target = [Select Id, Name, Phone From Contact Where Id = :targetContactId]; 56 | System.debug('****' + target.name); 57 | this.targetContact = target; 58 | } 59 | } -------------------------------------------------------------------------------- /src/classes/LABJS_SimpleController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LABJS_SimpleControllerTest.cls: -------------------------------------------------------------------------------- 1 | @isTest 2 | public with sharing class LABJS_SimpleControllerTest { 3 | 4 | static testmethod void testContacts() { 5 | Contact c = new Contact( firstname = 'Test', lastname='contact' ); 6 | insert c; 7 | LABJS_SimpleController sc = new LABJS_SimpleController(); 8 | system.assert(sc.getContacts().size() == 1); 9 | 10 | String contactsJSON = sc.getContactsJson(); 11 | List contactsDeserialized = (List) JSON.deserialize(contactsJSON, List.class); 12 | System.assert(contactsDeserialized.size() == 1); 13 | } 14 | 15 | static testmethod void testContactLeadSources() { 16 | LABJS_SimpleController sc = new LABJS_SimpleController(); 17 | system.assert(sc.getLeadSources().size() > 1); 18 | Contact c = new Contact( firstname = 'Test', lastname='contact', leadsource='web' ); 19 | insert c; 20 | 21 | sc.targetContactId = c.id; 22 | sc.targetContactLeadSource = 'phone'; 23 | sc.updateContactLeadSource(); 24 | c = [Select Id, LeadSource From Contact Where Id =: c.id]; 25 | System.assert(c.leadsource == 'phone'); 26 | 27 | } 28 | 29 | static testmethod void testSortableIndex() { 30 | LABJS_SimpleController sc = new LABJS_SimpleController(); 31 | system.assert(sc.getLeadSources().size() > 1); 32 | Contact c = new Contact( firstname = 'Test', lastname='contact', leadsource='web', sortable_index__c = 5 ); 33 | insert c; 34 | 35 | sc.contactSortableIndexJsonString = c.id; 36 | sc.updateContactSortableIndex(); 37 | 38 | c = [select id, sortable_index__c from Contact Where id = :c.id]; 39 | System.assert(c.sortable_index__c == 1); 40 | } 41 | 42 | static testmethod void testUpdateTargetContact() { 43 | Contact c = new Contact( firstname = 'Test', lastname='contact', leadsource='web', sortable_index__c = 5 ); 44 | insert c; 45 | 46 | LABJS_SimpleController sc = new LABJS_SimpleController(); 47 | System.assert(sc.targetContact == null); 48 | 49 | sc.targetContactId = c.id; 50 | sc.updateTargetContact(); 51 | System.assert(sc.targetContact != null); 52 | System.assert(sc.targetContact.id == c.id); 53 | } 54 | } -------------------------------------------------------------------------------- /src/classes/LABJS_SimpleControllerTest.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/RemoteTKController.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, salesforce.com, inc. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 | * that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 | * following disclaimer. 10 | * 11 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 | * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | * 14 | * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 | * promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | public class RemoteTKController { 28 | private static String makeError(String message, String errorCode) { 29 | JSONGenerator gen = JSON.createGenerator(false); 30 | gen.writeStartArray(); 31 | gen.writeStartObject(); 32 | gen.writeStringField('message', message); 33 | gen.writeStringField('errorCode', errorCode); 34 | gen.writeEndObject(); 35 | gen.writeEndArray(); 36 | 37 | return gen.getAsString(); 38 | } 39 | 40 | @remoteAction 41 | public static String describe(String objtype) { 42 | // Just enough to make the sample app work! 43 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 44 | if (targetType == null) { 45 | return makeError('The requested resource does not exist', 'NOT_FOUND'); 46 | } 47 | 48 | Schema.DescribeSObjectResult sobjResult = targetType.getDescribe(); 49 | 50 | Map fieldMap = sobjResult.fields.getMap(); 51 | 52 | List fields = new List(); 53 | for (String key : fieldMap.keySet()) { 54 | Schema.DescribeFieldResult descField = fieldMap.get(key).getDescribe(); 55 | Map field = new Map(); 56 | 57 | field.put('type', descField.getType().name().toLowerCase()); 58 | field.put('name', descField.getName()); 59 | field.put('label', descField.getLabel()); 60 | List references = new List(); 61 | for (Schema.sObjectType t: descField.getReferenceTo()) { 62 | references.add(t.getDescribe().getName()); 63 | } 64 | if (!references.isEmpty()) { 65 | field.put('referenceTo', references); 66 | } 67 | 68 | fields.add(field); 69 | } 70 | 71 | Map result = new Map(); 72 | result.put('fields', fields); 73 | 74 | return JSON.serialize(result); 75 | } 76 | 77 | @remoteAction 78 | public static String create(String objtype, String fields) { 79 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 80 | if (targetType == null) { 81 | return makeError('The requested resource does not exist', 'NOT_FOUND'); 82 | } 83 | 84 | SObject obj = targetType.newSObject(); 85 | 86 | Map fieldMap = null; 87 | try { 88 | fieldMap = (Map)JSON.deserializeUntyped(fields); 89 | } catch (JSONException je) { 90 | return makeError(je.getMessage(), 'JSON_PARSER_ERROR'); 91 | } 92 | 93 | Map targetFields = targetType.getDescribe().fields.getMap(); 94 | 95 | try { 96 | for (String key : fieldMap.keySet()) { 97 | if (targetFields.get(key) == null) { 98 | return '[{"message":"Field '+key+' does not exist on object type '+objtype+'","errorCode":"INVALID_FIELD"}]'; 99 | } 100 | 101 | if (targetFields.get(key).getDescribe().getType() == Schema.DisplayType.Date) { 102 | obj.put(key, Date.valueOf((String)fieldMap.get(key))); 103 | } else if (targetFields.get(key).getDescribe().getType() == Schema.DisplayType.Percent || 104 | targetFields.get(key).getDescribe().getType() == Schema.DisplayType.Currency) { 105 | obj.put(key, String.valueOf(fieldMap.get(key)) == '' ? null : Decimal.valueOf((String)fieldMap.get(key))); 106 | } else if (targetFields.get(key).getDescribe().getType() == Schema.DisplayType.Double) { 107 | obj.put(key, String.valueOf(fieldMap.get(key)) == '' ? null : Double.valueOf(fieldMap.get(key))); 108 | } else if (targetFields.get(key).getDescribe().getType() == Schema.DisplayType.Integer) { 109 | obj.put(key, Integer.valueOf(fieldMap.get(key))); 110 | } else { 111 | obj.put(key, fieldMap.get(key)); 112 | } 113 | } 114 | } catch (SObjectException soe) { 115 | return makeError(soe.getMessage(), 'INVALID_FIELD'); 116 | } 117 | 118 | try { 119 | insert obj; 120 | } catch (DMLException dmle) { 121 | String fieldNames = ''; 122 | for (String field : dmle.getDmlFieldNames(0)) { 123 | if (fieldNames.length() > 0) { 124 | fieldNames += ','; 125 | } 126 | fieldNames += '"'+field+'"'; 127 | } 128 | return '[{"fields":['+fieldNames+'],"message":"'+dmle.getDmlMessage(0)+'","errorCode":"'+dmle.getDmlType(0).name()+'"}]'; 129 | } 130 | 131 | Map result = new Map(); 132 | result.put('id', obj.id); 133 | result.put('errors', new List()); 134 | result.put('success', true); 135 | 136 | return JSON.serialize(result); 137 | } 138 | 139 | @remoteAction 140 | public static String retrieve(String objtype, String id, String fieldlist) { 141 | // TODO - handle null fieldlist - retrieve all fields 142 | Boolean containsId = false; 143 | for (String field : fieldlist.split(',')) { 144 | if (field.equalsIgnoreCase('id')){ 145 | containsId = true; 146 | break; 147 | } 148 | } 149 | 150 | if (!containsId) { 151 | fieldlist = 'Id,'+fieldlist; 152 | } 153 | 154 | String soql = 'SELECT '+fieldlist+' FROM '+objtype+' WHERE Id = \''+id+'\''; 155 | List records; 156 | try { 157 | records = Database.query(soql); 158 | } catch (QueryException qe) { 159 | return makeError(qe.getMessage(), 'INVALID_QUERY'); 160 | } 161 | 162 | return JSON.serialize(records[0]); 163 | } 164 | 165 | @remoteAction 166 | public static String upser(String objtype, String externalIdField, String externalId, String fields) { 167 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 168 | if (targetType == null) { 169 | return makeError('The requested resource does not exist', 'NOT_FOUND'); 170 | } 171 | 172 | SObject obj = targetType.newSObject(); 173 | obj.put(externalIdField, externalId); 174 | 175 | Map fieldMap = 176 | (Map)JSON.deserializeUntyped(fields); 177 | try { 178 | for (String key : fieldMap.keySet()) { 179 | obj.put(key, fieldMap.get(key)); 180 | } 181 | } catch (SObjectException soe) { 182 | return makeError(soe.getMessage(), 'INVALID_FIELD'); 183 | } 184 | 185 | Schema.SObjectField sobjField = targetType.getDescribe().fields.getMap().get(externalIdField); 186 | 187 | Database.Upsert(obj, sobjField); 188 | 189 | return null; 190 | } 191 | 192 | @remoteAction 193 | public static String updat(String objtype, String id, String fields) { 194 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 195 | if (targetType == null) { 196 | return makeError('The requested resource does not exist', 'NOT_FOUND'); 197 | } 198 | 199 | SObject obj = targetType.newSObject(id); 200 | 201 | Map fieldMap = null; 202 | try { 203 | fieldMap = (Map)JSON.deserializeUntyped(fields); 204 | } catch (JSONException je) { 205 | return makeError(je.getMessage(), 'JSON_PARSER_ERROR'); 206 | } 207 | 208 | try { 209 | for (String key : fieldMap.keySet()) { 210 | obj.put(key, fieldMap.get(key)); 211 | } 212 | } catch (SObjectException soe) { 213 | return makeError(soe.getMessage(), 'INVALID_FIELD'); 214 | } 215 | 216 | try { 217 | update obj; 218 | } catch (DMLException dmle) { 219 | String fieldNames = ''; 220 | for (String field : dmle.getDmlFieldNames(0)) { 221 | if (fieldNames.length() > 0) { 222 | fieldNames += ','; 223 | } 224 | fieldNames += '"'+field+'"'; 225 | } 226 | return '[{"fields":['+fieldNames+'],"message":"'+dmle.getDmlMessage(0)+'","errorCode":"'+dmle.getDmlType(0).name()+'"}]'; 227 | } 228 | 229 | return null; 230 | } 231 | 232 | @remoteAction 233 | public static String del(String objtype, String id) { 234 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 235 | if (targetType == null) { 236 | return makeError('The requested resource does not exist', 'NOT_FOUND'); 237 | } 238 | 239 | SObject obj = targetType.newSObject(id); 240 | 241 | try { 242 | delete obj; 243 | } catch (DMLException dmle) { 244 | String fieldNames = ''; 245 | for (String field : dmle.getDmlFieldNames(0)) { 246 | if (fieldNames.length() > 0) { 247 | fieldNames += ','; 248 | } 249 | fieldNames += '"'+field+'"'; 250 | } 251 | return '[{"fields":['+fieldNames+'],"message":"'+dmle.getDmlMessage(0)+'","errorCode":"'+dmle.getDmlType(0).name()+'"}]'; 252 | } 253 | 254 | return null; 255 | } 256 | 257 | @remoteAction 258 | public static String query(String soql) { 259 | List records; 260 | try { 261 | records = Database.query(soql); 262 | } catch (QueryException qe) { 263 | return makeError(qe.getMessage(), 'INVALID_QUERY'); 264 | } 265 | 266 | Map result = new Map(); 267 | result.put('records', records); 268 | result.put('totalSize', records.size()); 269 | result.put('done', true); 270 | 271 | return JSON.serialize(result); 272 | } 273 | 274 | @remoteAction 275 | public static String search(String sosl) { 276 | List> result; 277 | try { 278 | result = Search.query(sosl); 279 | } catch (QueryException qe) { 280 | return makeError(qe.getMessage(), 'INVALID_SEARCH'); 281 | } catch (SearchException se) { 282 | return makeError(se.getMessage(), 'INVALID_SEARCH'); 283 | } 284 | 285 | return JSON.serialize(result); 286 | } 287 | } -------------------------------------------------------------------------------- /src/classes/RemoteTKController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/TestRemoteTKController.cls: -------------------------------------------------------------------------------- 1 | @isTest 2 | public class TestRemoteTKController{ 3 | private static String tooLongAccName = 'LOTS OF '+ 4 | 'CHARACTERS XXXXXXXXXXXXXXXXXXXXXXXX'+ 5 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 6 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 7 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 8 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 9 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 10 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 11 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 12 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 13 | 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'+ 14 | 'XXXXXXXXXXXXXXXX'; 15 | 16 | static private void assertError(String jsonResult, String expectedError, String method) { 17 | List errorArray = (List)JSON.deserializeUntyped(jsonResult); 18 | 19 | System.assertNotEquals(null, errorArray, 20 | 'error array missing from '+method+' result'); 21 | System.assertNotEquals(0, errorArray.size(), 22 | 'error array is empty in '+method+' result'); 23 | 24 | Map error = (Map)errorArray[0]; 25 | String errorCode = (String)error.get('errorCode'); 26 | System.assertNotEquals(null, errorCode, 27 | 'errorCode property missing from '+method+' result'); 28 | System.assertEquals(expectedError, errorCode, 29 | 'errorCode should be '+expectedError+' in '+method+' result'); 30 | } 31 | 32 | static testMethod void testDescribe() { 33 | // Assume we have accounts 34 | String jsonResult = RemoteTKController.describe('Account'); 35 | 36 | System.assertNotEquals(null, jsonResult, 37 | 'RemoteTKController.describe returned null'); 38 | 39 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 40 | 41 | System.assertNotEquals(null, result.get('fields'), 42 | 'fields property missing from RemoteTKController.describe result'); 43 | 44 | // TODO - more assertions on describe results 45 | 46 | // Invalid object type 47 | // Hope there isn't a QXZXQZXZQXZQ object type! 48 | jsonResult = RemoteTKController.describe('QXZXQZXZQXZQ'); 49 | assertError(jsonResult, 'NOT_FOUND', 'RemoteTKController.describe'); 50 | } 51 | 52 | static private void assertRecord(Map record, String accName, String accNumber, String method) { 53 | Map attributes = (Map)record.get('attributes'); 54 | System.assertNotEquals(null, attributes, 55 | 'attributes property missing from '+method+' result'); 56 | System.assertNotEquals(0, attributes.keySet().size(), 57 | 'empty attributes object in '+method+' result'); 58 | 59 | String type = (String)attributes.get('type'); 60 | System.assertNotEquals(null, type, 61 | 'type property missing from '+method+' result'); 62 | System.assertEquals('Account', type, 63 | 'Wrong type in '+method+' result'); 64 | 65 | String url = (String)attributes.get('url'); 66 | System.assertNotEquals(null, url, 67 | 'url property missing from '+method+' result'); 68 | 69 | Id id = (Id)record.get('Id'); 70 | System.assertNotEquals(null, id, 71 | 'Id property missing from '+method+' result'); 72 | Account account = [SELECT Id, Name FROM Account WHERE Id = :id LIMIT 1]; 73 | System.assertNotEquals(null, account, 74 | 'Couldn\'t find account record identified by '+method+' result'); 75 | System.assertEquals(accName, account.Name, 76 | 'Account name doesn\'t match in '+method+' result'); 77 | 78 | String name = (String)record.get('Name'); 79 | System.assertNotEquals(null, name, 80 | 'Name property missing from '+method+' result'); 81 | System.assertEquals(accName, name, 82 | 'Wrong account name in '+method+' result'); 83 | 84 | String accountNumber = (String)record.get('AccountNumber'); 85 | System.assertNotEquals(null, name, 86 | 'AccountNumber property missing from '+method+' result'); 87 | System.assertEquals(accNumber, accountNumber, 88 | 'Wrong account number in '+method+' result'); 89 | } 90 | 91 | static private Id testCreate(String accName, String accNumber) { 92 | // Assume we can create an account 93 | String jsonResult = RemoteTKController.create('Account', '{"Name": "'+accName+'", "AccountNumber" : "'+accNumber+'"}'); 94 | 95 | System.assertNotEquals(null, jsonResult, 96 | 'RemoteTKController.create returned null'); 97 | 98 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 99 | 100 | Boolean success = (Boolean)result.get('success'); 101 | System.assertNotEquals(null, success, 102 | 'success property missing from RemoteTKController.create result'); 103 | System.assertNotEquals(false, success, 104 | 'success is false in RemoteTKController.create result'); 105 | 106 | List errors = (List)result.get('errors'); 107 | System.assertNotEquals(null, errors, 108 | 'errors property missing from RemoteTKController.create result'); 109 | System.assertEquals(0, errors.size(), 110 | 'errors array is not empty in RemoteTKController.create result'); 111 | 112 | Id id = (Id)result.get('id'); 113 | System.assertNotEquals(null, id, 114 | 'id property missing from RemoteTKController.create result'); 115 | Account account = [SELECT Id, Name, AccountNumber FROM Account LIMIT 1]; 116 | System.assertNotEquals(null, account, 117 | 'Couldn\'t find account record created by RemoteTKController.create result'); 118 | System.assertEquals(accName, account.Name, 119 | 'Account name doesn\'t match in RemoteTKController.create result'); 120 | System.assertEquals(accNumber, account.AccountNumber, 121 | 'Account number doesn\'t match in RemoteTKController.create result'); 122 | 123 | jsonResult = RemoteTKController.create('QXZXQZXZQXZQ', '{"Name": "'+accName+'"}'); 124 | assertError(jsonResult, 'NOT_FOUND', 'RemoteTKController.create'); 125 | 126 | jsonResult = RemoteTKController.create('Account', '{"Name" "'+accName+'"}'); 127 | assertError(jsonResult, 'JSON_PARSER_ERROR', 'RemoteTKController.create'); 128 | 129 | jsonResult = RemoteTKController.create('Account', '{"XQZXQZXQZXQZ" : "'+accName+'"}'); 130 | assertError(jsonResult, 'INVALID_FIELD', 'RemoteTKController.create'); 131 | 132 | jsonResult = RemoteTKController.create('Account', '{"Name" : "'+tooLongAccName+'"}'); 133 | assertError(jsonResult, 'STRING_TOO_LONG', 'RemoteTKController.create'); 134 | 135 | return id; 136 | } 137 | 138 | static private void testRetrieve(String accName, String accNumber, Id id) { 139 | String jsonResult = RemoteTKController.retrieve('Account', id, 'Name, AccountNumber'); 140 | 141 | System.assertNotEquals(null, jsonResult, 142 | 'RemoteTKController.retrieve returned null'); 143 | 144 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 145 | 146 | assertRecord(result, accName, accNumber, 'RemoteTKController.retrieve'); 147 | 148 | // TODO - test negative paths for retrieve 149 | } 150 | 151 | static private void testQuery(String accName, String accNumber) { 152 | String jsonResult = RemoteTKController.query('SELECT Id, Name, AccountNumber FROM Account WHERE Name = \''+accName+'\''); 153 | 154 | System.assertNotEquals(null, jsonResult, 155 | 'RemoteTKController.query returned null'); 156 | 157 | Map result = (Map)JSON.deserializeUntyped(jsonResult); 158 | 159 | List records = (List)result.get('records'); 160 | System.assertNotEquals(null, records, 161 | 'records property missing from RemoteTKController.query result'); 162 | System.assertEquals(1, records.size(), 163 | 'records array should have single record in RemoteTKController.query result'); 164 | 165 | Map record = (Map)records[0]; 166 | 167 | assertRecord(record, accName, accNumber, 'RemoteTKController.query'); 168 | 169 | Integer totalSize = (Integer)result.get('totalSize'); 170 | System.assertNotEquals(null, totalSize, 171 | 'totalSize property missing from RemoteTKController.query result'); 172 | System.assertEquals(1, totalSize, 173 | 'totalSize should be 1 in RemoteTKController.query result'); 174 | 175 | Boolean done = (Boolean)result.get('done'); 176 | System.assertNotEquals(null, done, 177 | 'done property missing from RemoteTKController.query result'); 178 | System.assertEquals(true, done, 179 | 'done should be true in RemoteTKController.query result'); 180 | 181 | jsonResult = RemoteTKController.query('SSSSSS Id, Name FROM Account WHERE Name = \''+accName+'\''); 182 | assertError(jsonResult, 'INVALID_QUERY', 'RemoteTKController.query'); 183 | } 184 | 185 | static private void testSearch(String accName, String accNumber, Id id) { 186 | Id [] fixedSearchResults= new Id[1]; 187 | fixedSearchResults[0] = id; 188 | Test.setFixedSearchResults(fixedSearchResults); 189 | String jsonResult = RemoteTKController.search('FIND {'+accName+'} IN ALL FIELDS RETURNING Account (Id, Name, AccountNumber)'); 190 | 191 | System.assertNotEquals(null, jsonResult, 192 | 'RemoteTKController.search returned null'); 193 | 194 | List result = (List)JSON.deserializeUntyped(jsonResult); 195 | 196 | List records = (List)result[0]; 197 | 198 | Map record = (Map)records[0]; 199 | 200 | assertRecord(record, accName, accNumber, 'RemoteTKController.search'); 201 | 202 | jsonResult = RemoteTKController.search('FFFF {'+accName+'} IN ALL FIELDS RETURNING Account (Id, Name)'); 203 | assertError(jsonResult, 'INVALID_SEARCH', 'RemoteTKController.search'); 204 | } 205 | 206 | static private void testUpdate(String accName, String accNumber, Id id) { 207 | String jsonResult = RemoteTKController.updat('Account', id, '{"Name":"'+accName+'1", "AccountNumber":"'+accNumber+'1"}'); 208 | System.assertEquals(null, jsonResult, 209 | 'Non-null result from RemoteTKController.updat'); 210 | Account account = [SELECT Id, Name, AccountNumber FROM Account WHERE Id = :id LIMIT 1]; 211 | System.assertNotEquals(null, account, 212 | 'Couldn\'t find account record after RemoteTKController.updat'); 213 | System.assertEquals(accName+'1', account.Name, 214 | 'Account name doesn\'t match after RemoteTKController.updat'); 215 | System.assertEquals(accNumber+'1', account.AccountNumber, 216 | 'Account number doesn\'t match after RemoteTKController.updat'); 217 | 218 | jsonResult = RemoteTKController.updat('QXZXQZXZQXZQ', id, '{"Name":"'+accName+'1"}'); 219 | assertError(jsonResult, 'NOT_FOUND', 'RemoteTKController.updat'); 220 | 221 | jsonResult = RemoteTKController.updat('Account', id, '{"XQZXQZXQZXQZ" : "'+accName+'1"}'); 222 | assertError(jsonResult, 'INVALID_FIELD', 'RemoteTKController.updat'); 223 | 224 | jsonResult = RemoteTKController.updat('Account', id, '{"Name" "'+accName+'"}'); 225 | assertError(jsonResult, 'JSON_PARSER_ERROR', 'RemoteTKController.updat'); 226 | 227 | jsonResult = RemoteTKController.updat('Account', id, '{"Name" : "'+tooLongAccName+'"}'); 228 | assertError(jsonResult, 'STRING_TOO_LONG', 'RemoteTKController.updat'); 229 | } 230 | 231 | static private void testUpsert(String accName, String accNumber, Id id) { 232 | String jsonResult = RemoteTKController.upser('Account', 'Id', (String)id, '{"Name":"'+accName+'2", "AccountNumber":"'+accNumber+'2"}'); 233 | System.assertEquals(null, jsonResult, 234 | 'Non-null result from RemoteTKController.upser'); 235 | Account account = [SELECT Id, Name, AccountNumber FROM Account WHERE Id = :id LIMIT 1]; 236 | System.assertNotEquals(null, account, 237 | 'Couldn\'t find account record after RemoteTKController.upser'); 238 | System.assertEquals(accName+'2', account.Name, 239 | 'Account name doesn\'t match after RemoteTKController.upser'); 240 | System.assertEquals(accNumber+'2', account.AccountNumber, 241 | 'Account number doesn\'t match after RemoteTKController.upser'); 242 | 243 | jsonResult = RemoteTKController.upser('QXZXQZXZQXZQ', 'Id', (String)id, '{"Name":"'+accName+'2"}'); 244 | assertError(jsonResult, 'NOT_FOUND', 'RemoteTKController.upser'); 245 | 246 | jsonResult = RemoteTKController.upser('Account', 'Id', (String)id, '{"XQZXQZXQZXQZ" : "'+accName+'2"}'); 247 | assertError(jsonResult, 'INVALID_FIELD', 'RemoteTKController.upser'); 248 | } 249 | 250 | static private void testDelete(Id id) { 251 | String jsonResult = RemoteTKController.del('QXZXQZXZQXZQ', id); 252 | assertError(jsonResult, 'NOT_FOUND', 'RemoteTKController.del'); 253 | 254 | jsonResult = RemoteTKController.del('Account', id); 255 | System.assertEquals(null, jsonResult, 256 | 'Non-null result from RemoteTKController.del'); 257 | List accounts = [SELECT Id, Name FROM Account WHERE Id = :id]; 258 | System.assertEquals(0, accounts.size(), 259 | 'Account record was not deleted by RemoteTKController.del'); 260 | 261 | jsonResult = RemoteTKController.del('Account', id); 262 | assertError(jsonResult, 'ENTITY_IS_DELETED', 'RemoteTKController.del'); 263 | } 264 | 265 | static testMethod void testCRUD() { 266 | String accName = 'Test1'; 267 | String accNumber = '1234'; 268 | 269 | Id id = testCreate(accName, accNumber); 270 | testRetrieve(accName, accNumber, id); 271 | testQuery(accName, accNumber); 272 | testSearch(accName, accNumber, id); 273 | testUpdate(accName, accNumber, id); 274 | testUpsert(accName, accNumber, id); 275 | testDelete(id); 276 | } 277 | } -------------------------------------------------------------------------------- /src/classes/TestRemoteTKController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/ngForceController.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Not all of these are used in this project, and as such some are commented out to positively 3 | * affect code coverage. 4 | */ 5 | global class ngForceController { 6 | 7 | public class picklistValues{ 8 | String label {get; set;} 9 | String value {get; set;} 10 | } 11 | 12 | public class QueryString { 13 | String selectClause {get; set;} 14 | String fromClause {get; set;} 15 | } 16 | 17 | @remoteAction 18 | public static String describe(String objtype) { 19 | // Just enough to make the sample app work! 20 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 21 | if (targetType == null) { 22 | return '[{"message":"The requested resource does not exist","errorCode":"NOT_FOUND"}]'; 23 | } 24 | 25 | Schema.DescribeSObjectResult sobjResult = targetType.getDescribe(); 26 | 27 | Map fieldMap = sobjResult.fields.getMap(); 28 | 29 | List fields = new List(); 30 | for (String key : fieldMap.keySet()) { 31 | Schema.DescribeFieldResult descField = fieldMap.get(key).getDescribe(); 32 | Map field = new Map(); 33 | 34 | field.put('type', descField.getType().name().toLowerCase()); 35 | field.put('name', descField.getName()); 36 | field.put('label', descField.getLabel()); 37 | List references = new List(); 38 | for (Schema.sObjectType t: descField.getReferenceTo()) { 39 | references.add(t.getDescribe().getName()); 40 | } 41 | if (!references.isEmpty()) { 42 | field.put('referenceTo', references); 43 | } 44 | 45 | fields.add(field); 46 | } 47 | 48 | Map result = new Map(); 49 | result.put('fields', fields); 50 | 51 | return JSON.serialize(result); 52 | } 53 | 54 | @remoteAction 55 | public static String bulkCreate(String objtype, String fields) { 56 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 57 | if (targetType == null) { 58 | return makeError('The requested resource does not exist', 'NOT_FOUND'); 59 | } 60 | 61 | Map targetFields = targetType.getDescribe().fields.getMap(); 62 | 63 | List objs = new List(); 64 | 65 | Map incomingFieldJsonObject = null; 66 | try { 67 | incomingFieldJsonObject = (Map)JSON.deserializeUntyped(fields); 68 | } catch (JSONException je) { 69 | return makeError('Unable to deserialize the fields object', 'INVALID_JSON'); 70 | } 71 | 72 | for(String row: incomingFieldJsonObject.keySet()){ 73 | Map current = (Map) incomingFieldJsonObject.get(row); 74 | String currentAsJson = JSON.serialize(current); 75 | SObject obj = targetType.newSObject(); 76 | String error = writeFields(objtype, obj, currentAsJson); 77 | if(error != null){ 78 | return error; 79 | } 80 | 81 | objs.add(obj); 82 | } 83 | 84 | try { 85 | insert objs; 86 | } catch (DMLException dmle) { 87 | String fieldNames = ''; 88 | for (String field : dmle.getDmlFieldNames(0)) { 89 | if (fieldNames.length() > 0) { 90 | fieldNames += ','; 91 | } 92 | fieldNames += '"'+field+'"'; 93 | } 94 | return '[{"fields":['+fieldNames+'],"message":"'+dmle.getDmlMessage(0)+'","errorCode":"'+dmle.getDmlType(0).name()+'"}]'; 95 | } 96 | 97 | List rids = new List(); 98 | for(sObject o : objs) { 99 | rids.add(o.Id); 100 | } 101 | 102 | Map result = new Map(); 103 | result.put('id', rids); 104 | result.put('errors', new List()); 105 | result.put('success', true); 106 | 107 | return JSON.serialize(result); 108 | } 109 | 110 | @remoteAction 111 | public static String describeFieldSet(String objtype, String fieldSetName) { 112 | Schema.SObjectType token = Schema.getGlobalDescribe().get(objtype); 113 | Schema.DescribeSObjectResult dr = token.getDescribe(); 114 | Map FsMap = dr.fieldSets.getMap(); 115 | return JSON.serialize(FsMap.get(FieldSetName).getFields()); 116 | } 117 | 118 | @remoteAction 119 | public static String soqlFromFieldSet(String objtype, String fieldSetName) { 120 | Schema.SObjectType token = Schema.getGlobalDescribe().get(objtype); 121 | Schema.DescribeSObjectResult dr = token.getDescribe(); 122 | Map FsMap = dr.fieldSets.getMap(); 123 | String selectClause = 'SELECT Id'; 124 | Set querySet = new Set(); 125 | querySet.addAll(FsMap.get(FieldSetName).getFields()); 126 | for(Schema.FieldSetMember f : querySet) { 127 | selectClause += ', '+ f.getFieldPath(); 128 | } 129 | String fromClause = ' FROM ' + dr.getName(); 130 | QueryString qs = new QueryString(); 131 | qs.selectClause = selectClause; 132 | qs.fromClause = fromClause; 133 | return JSON.serialize(qs); 134 | } 135 | 136 | @remoteAction 137 | public static String queryFromFieldSet(String id, String fieldSetName){ 138 | Id objId = (id) Id; 139 | Schema.SObjectType token = objId.getSObjectType(); 140 | Schema.DescribeSObjectResult dr = token.getDescribe(); 141 | Map FsMap = dr.fieldSets.getMap(); 142 | String queryString = 'SELECT Id'; 143 | List querySet = new List(); 144 | querySet = FsMap.get(FieldSetName).getFields(); 145 | for(Schema.FieldSetMember f : querySet) { 146 | queryString += ', '+ f.getFieldPath(); 147 | } 148 | queryString += ' FROM ' + dr.getName() + ' WHERE id = \''+ objId +'\' LIMIT 1'; 149 | system.debug(queryString); 150 | return JSON.serialize(database.Query(queryString)); 151 | } 152 | 153 | @remoteAction 154 | public static String getPicklistValues(String objtype, String fieldName) { 155 | Schema.SObjectType token = Schema.getGlobalDescribe().get(objtype); 156 | Schema.DescribeSObjectResult dr = token.getDescribe(); 157 | Map field_map = dr.fields.getMap(); 158 | List pickListValues = field_map.get(fieldName).getDescribe().getPickListValues(); 159 | List options = new List(); 160 | for(Schema.PicklistEntry plv : pickListValues){ 161 | pickListValues pv = new pickListValues(); 162 | pv.label = plv.getLabel(); 163 | pv.value = plv.getValue(); 164 | options.add(pv); 165 | } 166 | return JSON.serialize(options); 167 | } 168 | 169 | @remoteAction 170 | public static String getObjType(String Id) { 171 | Id objId = (id) Id; 172 | Schema.SObjectType token = objId.getSObjectType(); 173 | Schema.DescribeSObjectResult dr = token.getDescribe(); 174 | String objName = dr.getName(); 175 | Map retObj = new Map(); 176 | retObj.put('type', objName); 177 | return JSON.serialize(retObj); 178 | } 179 | 180 | @remoteAction 181 | public static String create(String objtype, String fields) { 182 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 183 | if (targetType == null) { 184 | return makeError('The requested resource does not exist', 'NOT_FOUND'); 185 | } 186 | 187 | SObject obj = targetType.newSObject(); 188 | 189 | String error = writeFields(objType, obj, fields); 190 | if (error != null) { 191 | return error; 192 | } 193 | 194 | try { 195 | insert obj; 196 | } catch (DMLException dmle) { 197 | String fieldNames = ''; 198 | for (String field : dmle.getDmlFieldNames(0)) { 199 | if (fieldNames.length() > 0) { 200 | fieldNames += ','; 201 | } 202 | fieldNames += '"'+field+'"'; 203 | } 204 | return '[{"fields":['+fieldNames+'],"message":"'+dmle.getDmlMessage(0)+'","errorCode":"'+dmle.getDmlType(0).name()+'"}]'; 205 | } 206 | 207 | Map result = new Map(); 208 | result.put('id', obj.id); 209 | result.put('errors', new List()); 210 | result.put('success', true); 211 | 212 | return JSON.serialize(result); 213 | 214 | } 215 | 216 | @remoteAction 217 | public static String retrieve(String objtype, String id, String fieldlist) { 218 | // TODO - handle null fieldlist - retrieve all fields 219 | Boolean containsId = false; 220 | for (String field : fieldlist.split(',')) { 221 | if (field.equalsIgnoreCase('id')){ 222 | containsId = true; 223 | break; 224 | } 225 | } 226 | 227 | if (!containsId) { 228 | fieldlist = 'Id,'+fieldlist; 229 | } 230 | 231 | String soql = 'SELECT '+fieldlist+' FROM '+objtype+' WHERE Id = \''+id+'\''; 232 | List records; 233 | try { 234 | records = Database.query(soql); 235 | } catch (QueryException qe) { 236 | return '[{"message":"'+qe.getMessage()+'","errorCode":"INVALID_QUERY"}]'; 237 | } 238 | 239 | return JSON.serialize(records[0]); 240 | } 241 | 242 | @remoteAction 243 | public static String upser(String objtype, String externalIdField, String externalId, String fields) { 244 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 245 | if (targetType == null) { 246 | return makeError('The requested resource does not exist', 'NOT_FOUND'); 247 | } 248 | 249 | SObject obj = targetType.newSObject(); 250 | obj.put(externalIdField, externalId); 251 | 252 | String error = writeFields(objType, obj, fields); 253 | if (error != null) { 254 | return error; 255 | } 256 | 257 | Schema.SObjectField sobjField = targetType.getDescribe().fields.getMap().get(externalIdField); 258 | 259 | Database.Upsert(obj, sobjField); 260 | 261 | return null; 262 | } 263 | 264 | @remoteAction 265 | public static String updat(String objtype, String id, String fields) { 266 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 267 | if (targetType == null) { 268 | return '[{"message":"The requested resource does not exist","errorCode":"NOT_FOUND"}]'; 269 | } 270 | 271 | SObject obj = targetType.newSObject(id); 272 | 273 | Map fieldMap = null; 274 | try { 275 | fieldMap = (Map)JSON.deserializeUntyped(fields); 276 | } catch (JSONException je) { 277 | return '[{"message":"'+je.getMessage()+'","errorCode":"JSON_PARSER_ERROR"}]'; 278 | } 279 | 280 | try { 281 | for (String key : fieldMap.keySet()) { 282 | obj.put(key, fieldMap.get(key)); 283 | } 284 | } catch (SObjectException soe) { 285 | return '[{"message":"'+soe.getMessage()+'","errorCode":"INVALID_FIELD"}]'; 286 | } 287 | 288 | try { 289 | update obj; 290 | } catch (DMLException dmle) { 291 | String fieldNames = ''; 292 | for (String field : dmle.getDmlFieldNames(0)) { 293 | if (fieldNames.length() > 0) { 294 | fieldNames += ','; 295 | } 296 | fieldNames += '"'+field+'"'; 297 | } 298 | return '[{"fields":['+fieldNames+'],"message":"'+dmle.getDmlMessage(0)+'","errorCode":"'+dmle.getDmlType(0).name()+'"}]'; 299 | } 300 | 301 | return null; 302 | } 303 | 304 | @remoteAction 305 | public static String del(String objtype, String id) { 306 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 307 | if (targetType == null) { 308 | return '[{"message":"The requested resource does not exist","errorCode":"NOT_FOUND"}]'; 309 | } 310 | 311 | SObject obj = targetType.newSObject(id); 312 | 313 | try { 314 | delete obj; 315 | } catch (DMLException dmle) { 316 | String fieldNames = ''; 317 | for (String field : dmle.getDmlFieldNames(0)) { 318 | if (fieldNames.length() > 0) { 319 | fieldNames += ','; 320 | } 321 | fieldNames += '"'+field+'"'; 322 | } 323 | return '[{"fields":['+fieldNames+'],"message":"'+dmle.getDmlMessage(0)+'","errorCode":"'+dmle.getDmlType(0).name()+'"}]'; 324 | } 325 | 326 | return null; 327 | } 328 | 329 | @remoteAction 330 | public static String query(String soql) { 331 | List records; 332 | try { 333 | records = Database.query(soql); 334 | } catch (QueryException qe) { 335 | return '[{"message":"'+qe.getMessage()+'","errorCode":"INVALID_QUERY"}]'; 336 | } 337 | 338 | Map result = new Map(); 339 | result.put('records', records); 340 | result.put('totalSize', records.size()); 341 | result.put('done', true); 342 | 343 | return JSON.serialize(result); 344 | } 345 | 346 | @remoteAction 347 | public static String search(String sosl) { 348 | List> result; 349 | try { 350 | result = Search.query(sosl); 351 | } catch (QueryException qe) { 352 | return makeError(qe.getMessage(), 'INVALID_SEARCH'); 353 | } catch (SearchException se) { 354 | return makeError(se.getMessage(), 'INVALID_SEARCH'); 355 | } 356 | 357 | return JSON.serialize(result); 358 | } 359 | 360 | /* 361 | * Helper Methods 362 | */ 363 | private static String makeError(String message, String errorCode) { 364 | JSONGenerator gen = JSON.createGenerator(false); 365 | gen.writeStartArray(); 366 | gen.writeStartObject(); 367 | gen.writeStringField('message', message); 368 | gen.writeStringField('errorCode', errorCode); 369 | gen.writeEndObject(); 370 | gen.writeEndArray(); 371 | 372 | return gen.getAsString(); 373 | } 374 | 375 | private static String writeFields(String objtype, SObject obj, String fields) { 376 | Map fieldMap = null; 377 | try { 378 | fieldMap = (Map)JSON.deserializeUntyped(fields); 379 | } catch (JSONException je) { 380 | return makeError(je.getMessage(), 'JSON_PARSER_ERROR'); 381 | } 382 | 383 | Schema.SObjectType targetType = Schema.getGlobalDescribe().get(objtype); 384 | 385 | Map targetFields = targetType.getDescribe().fields.getMap(); 386 | 387 | try { 388 | for (String key : fieldMap.keySet()) { 389 | if (targetFields.get(key) == null) { 390 | return '[{"message":"Field '+key+' does not exist on object type '+objtype+'","errorCode":"INVALID_FIELD"}]'; 391 | } 392 | 393 | Object value = fieldMap.get(key); 394 | Schema.DisplayType valueType = targetFields.get(key).getDescribe().getType(); 395 | system.debug('####################### Key = ' + key + ' ValueType: ' + valueType); 396 | if (value instanceof String && valueType != Schema.DisplayType.String) { 397 | // Coerce an incoming String to the correct type 398 | String svalue = (String)value; 399 | 400 | if (valueType == Schema.DisplayType.Date) { 401 | obj.put(key, Date.valueOf(svalue)); 402 | } else if(valueType == Schema.DisplayType.DateTime){ 403 | obj.put(key, DateTime.valueOfGmt(svalue)); 404 | } else if (valueType == Schema.DisplayType.Percent || 405 | valueType == Schema.DisplayType.Currency) { 406 | obj.put(key, svalue == '' ? null : Decimal.valueOf(svalue)); 407 | } else if (valueType == Schema.DisplayType.Double) { 408 | obj.put(key, svalue == '' ? null : Double.valueOf(svalue)); 409 | } else if (valueType == Schema.DisplayType.Integer) { 410 | obj.put(key, Integer.valueOf(svalue)); 411 | } else if (valueType == Schema.DisplayType.Base64) { 412 | obj.put(key, Blob.valueOf(svalue)); 413 | } else { 414 | obj.put(key, svalue); 415 | } 416 | } else { 417 | // Just try putting the incoming value on the object 418 | obj.put(key, value); 419 | } 420 | } 421 | } catch (SObjectException soe) { 422 | return makeError(soe.getMessage(), 'INVALID_FIELD'); 423 | } 424 | 425 | return null; 426 | } 427 | 428 | } -------------------------------------------------------------------------------- /src/classes/ngForceController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/components/LABJS_RemoteTK.component: -------------------------------------------------------------------------------- 1 | 26 | 27 | 308 | -------------------------------------------------------------------------------- /src/components/LABJS_RemoteTK.component-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/components/LABJS_ngforce.component: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/components/LABJS_ngforce.component-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/documents/LAB_JAVASCRIPT_PLAYGROUND_FOLDER-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | LAB_JAVASCRIPT_PLAYGROUND_FOLDER 5 | ReadOnly 6 | 7 | -------------------------------------------------------------------------------- /src/documents/LAB_JAVASCRIPT_PLAYGROUND_FOLDER/LAB_JAVASCRIPT_PLAYGROUND.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/documents/LAB_JAVASCRIPT_PLAYGROUND_FOLDER/LAB_JAVASCRIPT_PLAYGROUND.png -------------------------------------------------------------------------------- /src/documents/LAB_JAVASCRIPT_PLAYGROUND_FOLDER/LAB_JAVASCRIPT_PLAYGROUND.png-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | LAB_JAVASCRIPT_PLAYGROUND 5 | true 6 | 7 | -------------------------------------------------------------------------------- /src/layouts/LABJS_Mo_Tester_2__c-Mo Tester 2 Layout.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | false 6 | true 7 | 8 | 9 | 10 | Required 11 | Name 12 | 13 | 14 | 15 | 16 | Edit 17 | OwnerId 18 | 19 | 20 | 21 | 22 | 23 | false 24 | false 25 | true 26 | 27 | 28 | 29 | Readonly 30 | CreatedById 31 | 32 | 33 | 34 | 35 | Readonly 36 | LastModifiedById 37 | 38 | 39 | 40 | 41 | 42 | false 43 | false 44 | true 45 | 46 | 47 | 48 | 49 | 50 | 51 | TASK.SUBJECT 52 | TASK.WHO_NAME 53 | ACTIVITY.TASK 54 | TASK.DUE_DATE 55 | TASK.STATUS 56 | TASK.PRIORITY 57 | CORE.USERS.FULL_NAME 58 | RelatedActivityList 59 | 60 | 61 | TASK.SUBJECT 62 | TASK.WHO_NAME 63 | ACTIVITY.TASK 64 | TASK.DUE_DATE 65 | CORE.USERS.FULL_NAME 66 | TASK.LAST_UPDATE 67 | RelatedHistoryList 68 | 69 | false 70 | true 71 | true 72 | false 73 | false 74 | 75 | -------------------------------------------------------------------------------- /src/objects/Contact.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sortable_Index__c 5 | false 6 | 7 | 5 8 | false 9 | 0 10 | false 11 | Number 12 | false 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/objects/LABJS_Mo_Tester_2__c.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Accept 5 | Default 6 | 7 | 8 | Clone 9 | Default 10 | 11 | 12 | Delete 13 | Default 14 | 15 | 16 | Edit 17 | Default 18 | 19 | 20 | List 21 | Default 22 | 23 | 24 | New 25 | Default 26 | 27 | 28 | Tab 29 | Default 30 | 31 | 32 | View 33 | Default 34 | 35 | Deployed 36 | true 37 | false 38 | false 39 | true 40 | true 41 | 42 | details 43 | 44 | MT2_Date_1__c 45 | false 46 | 47 | For Testing. 48 | 49 | MT2_Textarea_1__c 50 | false 51 | 52 | 53 | MT2_Phone_1__c 54 | false 55 | 56 | 57 | 58 | 59 | Account__c 60 | SetNull 61 | false 62 | 63 | Account 64 | Mo Tester 2s 65 | Mo_Tester_2s 66 | false 67 | false 68 | Lookup 69 | 70 | 71 | MT2_Checkbox_1__c 72 | false 73 | false 74 | 75 | false 76 | Checkbox 77 | 78 | 79 | MT2_Currency_1__c 80 | false 81 | 82 | 18 83 | false 84 | 2 85 | false 86 | Currency 87 | 88 | 89 | MT2_Date_1__c 90 | false 91 | 92 | false 93 | false 94 | Date 95 | 96 | 97 | MT2_Datetime_1__c 98 | false 99 | 100 | false 101 | false 102 | DateTime 103 | 104 | 105 | MT2_Email_1__c 106 | false 107 | 108 | false 109 | false 110 | Email 111 | false 112 | 113 | 114 | MT2_Geolocation_1__c 115 | false 116 | false 117 | 118 | false 119 | 5 120 | false 121 | Location 122 | 123 | 124 | MT2_Multi_Select_1__c 125 | false 126 | 127 | 128 | 129 | MT2 MS1 One 130 | false 131 | 132 | 133 | MT2 MS1 Two 134 | false 135 | 136 | 137 | MT2 MS1 Three 138 | false 139 | 140 | 141 | MT2 MS1 Four 142 | false 143 | 144 | false 145 | 146 | false 147 | MultiselectPicklist 148 | 4 149 | 150 | 151 | MT2_Number_1__c 152 | false 153 | 154 | 18 155 | false 156 | 8 157 | false 158 | Number 159 | false 160 | 161 | 162 | MT2_Percent_1__c 163 | false 164 | 165 | 12 166 | false 167 | 2 168 | false 169 | Percent 170 | 171 | 172 | MT2_Phone_1__c 173 | false 174 | 175 | false 176 | false 177 | Phone 178 | 179 | 180 | MT2_Picklist_1__c 181 | false 182 | 183 | 184 | 185 | MT2 P1 One 186 | false 187 | 188 | 189 | MT2 P1 Two 190 | false 191 | 192 | 193 | MT2 P1 Three 194 | false 195 | 196 | 197 | MT2 P1 Four 198 | false 199 | 200 | false 201 | 202 | false 203 | Picklist 204 | 205 | 206 | MT2_Text_1__c 207 | false 208 | 209 | 255 210 | false 211 | false 212 | Text 213 | false 214 | 215 | 216 | MT2_Text_Area_Long_1__c 217 | false 218 | 219 | 32768 220 | false 221 | LongTextArea 222 | 3 223 | 224 | 225 | MT2_Text_Area_Rich_1__c 226 | false 227 | 228 | 32768 229 | false 230 | Html 231 | 25 232 | 233 | 234 | MT2_Text_Encrypted_1__c 235 | false 236 | 237 | 25 238 | X 239 | lastFour 240 | false 241 | false 242 | EncryptedText 243 | 244 | 245 | MT2_Textarea_1__c 246 | false 247 | 248 | false 249 | false 250 | TextArea 251 | 252 | 253 | MT2_URL_1__c 254 | false 255 | 256 | false 257 | false 258 | Url 259 | 260 | 261 | 262 | All 263 | Everything 264 | 265 | 266 | 267 | 268 | false 269 | Text 270 | 271 | Mo Tester 2s 272 | 273 | ReadWrite 274 | 275 | -------------------------------------------------------------------------------- /src/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | LABJS Javascript Playground 4 | 5 | LABJS_CodeFriar_TestUtils 6 | LABJS_CodeFriar_ngForceController_TEST 7 | LABJS_SimpleController 8 | LABJS_SimpleControllerTest 9 | RemoteTKController 10 | TestRemoteTKController 11 | ngForceController 12 | ApexClass 13 | 14 | 15 | LABJS_RemoteTK 16 | LABJS_ngforce 17 | ApexComponent 18 | 19 | 20 | LABJS_Angular105 21 | LABJS_Angular105_ContactList 22 | LABJS_Angular105_ContactList_ForceTK 23 | LABJS_Angular105_Hello 24 | LABJS_Backbone10 25 | LABJS_Backbone10_Todos 26 | LABJS_Bootstrap231 27 | LABJS_Bootstrap231_Hello 28 | LABJS_ForceTK 29 | LABJS_ForceTK_AccountList 30 | LABJS_JQuery191 31 | LABJS_JQuery191_DragDrop 32 | LABJS_JQuery191_Simple 33 | LABJS_JQuery191_Sortable 34 | LABJS_JQueryMobile130 35 | LABJS_JQueryMobile130_Hello 36 | LABJS_ngForceDemo 37 | ApexPage 38 | 39 | 40 | LABJS_Javascript_Playground 41 | CustomApplication 42 | 43 | 44 | Contact.Sortable_Index__c 45 | LABJS_Mo_Tester_2__c.Account__c 46 | LABJS_Mo_Tester_2__c.MT2_Checkbox_1__c 47 | LABJS_Mo_Tester_2__c.MT2_Currency_1__c 48 | LABJS_Mo_Tester_2__c.MT2_Date_1__c 49 | LABJS_Mo_Tester_2__c.MT2_Datetime_1__c 50 | LABJS_Mo_Tester_2__c.MT2_Email_1__c 51 | LABJS_Mo_Tester_2__c.MT2_Geolocation_1__c 52 | LABJS_Mo_Tester_2__c.MT2_Multi_Select_1__c 53 | LABJS_Mo_Tester_2__c.MT2_Number_1__c 54 | LABJS_Mo_Tester_2__c.MT2_Percent_1__c 55 | LABJS_Mo_Tester_2__c.MT2_Phone_1__c 56 | LABJS_Mo_Tester_2__c.MT2_Picklist_1__c 57 | LABJS_Mo_Tester_2__c.MT2_Text_1__c 58 | LABJS_Mo_Tester_2__c.MT2_Text_Area_Long_1__c 59 | LABJS_Mo_Tester_2__c.MT2_Text_Area_Rich_1__c 60 | LABJS_Mo_Tester_2__c.MT2_Text_Encrypted_1__c 61 | LABJS_Mo_Tester_2__c.MT2_Textarea_1__c 62 | LABJS_Mo_Tester_2__c.MT2_URL_1__c 63 | CustomField 64 | 65 | 66 | LABJS_Mo_Tester_2__c 67 | CustomObject 68 | 69 | 70 | Backbone_1_0 71 | Bootstrap_2_3_1 72 | ForceTK 73 | JQuery_191_Examples 74 | JQuery_Mobile_130_Examples 75 | LABJS_AngularJS 76 | CustomTab 77 | 78 | 79 | LAB_JAVASCRIPT_PLAYGROUND_FOLDER 80 | LAB_JAVASCRIPT_PLAYGROUND_FOLDER/LAB_JAVASCRIPT_PLAYGROUND.png 81 | Document 82 | 83 | 84 | LABJS_Mo_Tester_2__c.details 85 | FieldSet 86 | 87 | 88 | LABJS_Mo_Tester_2__c-Mo Tester 2 Layout 89 | Layout 90 | 91 | 92 | LABJS_Mo_Tester_2__c.All 93 | ListView 94 | 95 | 96 | LABJS_Angular 97 | LABJS_Backbone 98 | LABJS_Bootstrap 99 | LABJS_ForceTK 100 | LABJS_jQuery 101 | LABJS_jQueryMobile 102 | LABJS_ngForce 103 | StaticResource 104 | 105 | 25.0 106 | 107 | -------------------------------------------------------------------------------- /src/pages/LABJS_Angular105.page: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Checkout the Mobile Pack Angular JS Samples

4 |

AngularJs.org

5 |

Hello, Angular

6 |

Contact List

7 |

Contact List, Powered by ForceTK & RemoteTK (see ForceTK tab for easy configuration)

8 |

ngForce Demo, from Kevin Poorman

9 |
10 |
-------------------------------------------------------------------------------- /src/pages/LABJS_Angular105.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_Angular105_ContactList.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 19 | 20 | 21 |

Declares an inline controller, grabs contacts as a JSON string, and displays them using an Angular repeat control with alternating styling, also courtesy of Angular.

22 |
23 | 24 | 25 |
26 |
27 |
    28 |
  • {{current.Name}}
  • 29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 |
-------------------------------------------------------------------------------- /src/pages/LABJS_Angular105_ContactList.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_Angular105_ContactList_ForceTK.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 37 | 38 | 39 |

Similar to the previous Angular, however this implementation has no Apex controller. 40 | It relies entirely on ForceTK and RemoteTK. Note that RemoteTKComponent has a minimum controller designed 41 | to give you access to core API functionality.

42 |
43 |
44 |
45 |
    46 |
  • {{current.Name}}
  • 47 |
48 | 49 |
50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 |
-------------------------------------------------------------------------------- /src/pages/LABJS_Angular105_ContactList_ForceTK.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_Angular105_Hello.page: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |
7 |

Hello {{yourName}}!

8 |
9 | 10 |
-------------------------------------------------------------------------------- /src/pages/LABJS_Angular105_Hello.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_Backbone10.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Backbonejs.org

5 |

Checkout Backbone Mobile Pack Examples

6 | 7 |

Standard //Todo list from Backbone.js

8 | 9 |
10 |
-------------------------------------------------------------------------------- /src/pages/LABJS_Backbone10.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_Backbone10_Todos.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

This is the standard example from Backbone 13 | on how to use their app, mildly adapted to Visualforce.

14 | 15 |
16 | 17 |
18 | 19 |
20 |

Todos

21 | 22 |
23 | 24 |
25 | 26 | 27 |
    28 |
    29 | 30 | 34 | 35 |
    36 | 37 |
    38 | Double-click to edit a todo. 39 |
    40 | 41 |
    42 | Created by 43 |
    44 | Jérôme Gravel-Niquet. 45 |
    Rewritten by: TodoMVC. 46 |
    47 | 48 | 49 | 50 | 51 | 52 | 60 | 61 | 67 | 68 | 69 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_Backbone10_Todos.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_Bootstrap231.page: -------------------------------------------------------------------------------- 1 | 2 | 3 |

    Bootstrap on Github

    4 |

    Hello, Bootstrap.

    5 |
    6 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_Bootstrap231.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_Bootstrap231_Hello.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 17 | 27 | 28 | 57 | 58 |
    59 | 60 |

    Bootstrap starter template

    61 |

    Use this document as a way to quick start any new project.
    All you get is this message and a barebones HTML document.

    62 | 63 |

    Be sure to look at the black bar up at the top.

    64 | 65 |

    Because that's the magic in a basic Bootstrap Hello Page.

    66 | 67 | 68 | Launch demo modal 69 | 70 |

    Select A Contact

    71 | 72 |

    Be sure to try the "Select a contact" drop down on the menu bar.

    73 | 74 |
    75 | 76 | 77 | 78 | 91 | 92 | 93 | 110 | 111 | 112 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_Bootstrap231_Hello.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_ForceTK.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

    The Force Toolkit is a great way to access Force.com REST services from Javascript. 5 | Read more on the Github Repo.

    6 | 7 |

    Note that you must add the REST endpoint hostname for your instance (i.e. 8 | https://na1.salesforce.com/ or similar) as a remote site - in the admin 9 | console, go to Your Name | Setup | Security Controls | Remote Site Settings

    10 | 11 |

    ForceTK Account List Basic Example Page

    12 | 13 | 14 |
    15 | 16 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_ForceTK.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_ForceTK_AccountList.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Force.com JavaScript REST Toolkit 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
    49 |
    50 |
    51 |
    52 |
    53 |
    54 | 55 | 72 | 73 | 85 | 86 | 107 | 108 | 120 | 121 | 122 | 123 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_ForceTK_AccountList.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_JQuery191.page: -------------------------------------------------------------------------------- 1 | 2 | 3 |

    Simple - change text background color with a button.

    4 |

    Drag and Drop Contacts on to Contact Lead Source values. Watch the event stream, updates Contact Lead Source.

    5 |

    Sortable - Drag Contacts into the Right order, save that order to the database.

    6 | 7 |
    8 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_JQuery191.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_JQuery191_DragDrop.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | done 13 | 14 | 15 |

    Pretty simple drag and drop. Watch the events roll by in the black box on the right. 16 | Note how the drop event calls the "dropLog" with details about what was dropped on what. 17 | ALSO, once dropped, the app updates the contact with the selected lead source. Reload to verify. 18 | Back to JQuery 191 Demos

    19 |
    20 | 28 | 54 | 55 |
    56 | 57 | 58 | 59 | 60 | 61 | 62 |

    ()

    63 |
    64 |
    65 |
    66 | 67 |
    68 | 69 | 70 | 71 |
    72 |

    {!Current}

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

    Drop History

    80 |
    81 | 82 | 83 | 84 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_JQuery191_DragDrop.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_JQuery191_Simple.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 27 | 28 |

    This changes the background color on the accounts to Red or White.   29 | Main JQuery 1.9.1 Page

    30 |
    31 | 32 |
    33 | 34 | 35 |
    36 | 37 | 38 |

    39 |
    40 | 41 |
    42 | 43 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_JQuery191_Simple.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_JQuery191_Sortable.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 17 | 51 | 52 | 53 |

    Simply sorts contacts based on your drag and drop approach. 54 | Back to JQuery 191 Demos

    55 | 56 |
    57 | 58 | 59 | done 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
    70 |
      71 | 72 |
    • ()
    • 73 |
      74 |
    75 |
    76 |
    77 |
    78 |

    Sort History

    79 |
    80 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_JQuery191_Sortable.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_JQueryMobile130.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

    Checkout the jQuery Mobile Pack

    5 | Hello, Contacts. See a list of contacts, click/tap on one to being up the detail screen. 6 | 7 |
    8 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_JQueryMobile130.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_JQueryMobile130_Hello.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 21 | 22 |
    23 | 24 |
    25 |

    Foo

    26 |
    27 | 28 |
    29 | 30 |
      31 | 32 |
    • 33 |
      34 |
    35 | 36 |
    37 | 38 |
    39 |

    Page Footer

    40 |
    41 |
    42 | 43 |
    44 | 45 |
    46 |

    Page Title

    47 |
    48 | 49 | 50 |
    51 | 52 | 53 | done 54 | 55 | 56 |

    Page content goes here.

    57 |

    Name:

    58 | 59 | 60 |
    61 |
    62 | 63 |
    64 |

    Page Footer

    65 |
    66 | 67 |
    68 | 69 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_JQueryMobile130_Hello.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_ngForceDemo.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 55 | 56 |
    57 |
    58 |
    59 | 70 |
    71 |
    72 | 73 |
    74 |
    75 | 76 |
    77 | 81 | 82 |
    83 | 84 | 104 | 105 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_ngForceDemo.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_oppBoxTmpl.page: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |
      5 |
    • 6 |
      7 | {{opp.StageName}} 8 |
      9 |

      {{opp.Account.Name}}

      10 |
      11 |
      12 | {{opp.Name}} 13 |
      14 |
      See Details
      15 |
      16 |
    • 17 |
    18 |
    19 |
    20 |
    -------------------------------------------------------------------------------- /src/pages/LABJS_oppBoxTmpl.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_oppDetail.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/pages/LABJS_oppDetail.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/pages/LABJS_oppDetailsTmpl.page: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 11 | -------------------------------------------------------------------------------- /src/pages/LABJS_oppDetailsTmpl.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/staticresources/LABJS_Angular.resource: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/staticresources/LABJS_Angular.resource -------------------------------------------------------------------------------- /src/staticresources/LABJS_Angular.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/zip 5 | 6 | -------------------------------------------------------------------------------- /src/staticresources/LABJS_Backbone.resource: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/staticresources/LABJS_Backbone.resource -------------------------------------------------------------------------------- /src/staticresources/LABJS_Backbone.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/zip 5 | 6 | -------------------------------------------------------------------------------- /src/staticresources/LABJS_Bootstrap.resource: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/staticresources/LABJS_Bootstrap.resource -------------------------------------------------------------------------------- /src/staticresources/LABJS_Bootstrap.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/zip 5 | 6 | -------------------------------------------------------------------------------- /src/staticresources/LABJS_ForceTK.resource: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/staticresources/LABJS_ForceTK.resource -------------------------------------------------------------------------------- /src/staticresources/LABJS_ForceTK.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/zip 5 | 6 | -------------------------------------------------------------------------------- /src/staticresources/LABJS_jQuery.resource: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/staticresources/LABJS_jQuery.resource -------------------------------------------------------------------------------- /src/staticresources/LABJS_jQuery.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/zip 5 | 6 | -------------------------------------------------------------------------------- /src/staticresources/LABJS_jQueryMobile.resource: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/staticresources/LABJS_jQueryMobile.resource -------------------------------------------------------------------------------- /src/staticresources/LABJS_jQueryMobile.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/zip 5 | 6 | -------------------------------------------------------------------------------- /src/staticresources/LABJS_ngForce.resource: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReidCarlberg/LAB-Javascript-Playground/2791437ea4c2a0b9a28fc3aa75fe01440882ff90/src/staticresources/LABJS_ngForce.resource -------------------------------------------------------------------------------- /src/staticresources/LABJS_ngForce.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | application/zip 5 | 6 | -------------------------------------------------------------------------------- /src/tabs/Backbone_1_0.tab: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | Custom72: Helicopter 6 | LABJS_Backbone10 7 | 8 | -------------------------------------------------------------------------------- /src/tabs/Bootstrap_2_3_1.tab: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | Custom75: IP Phone 6 | LABJS_Bootstrap231 7 | 8 | -------------------------------------------------------------------------------- /src/tabs/ForceTK.tab: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | Custom4: Hexagon 6 | LABJS_ForceTK 7 | 8 | -------------------------------------------------------------------------------- /src/tabs/JQuery_191_Examples.tab: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | Custom51: Apple 6 | LABJS_JQuery191 7 | 8 | -------------------------------------------------------------------------------- /src/tabs/JQuery_Mobile_130_Examples.tab: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | Custom33: Desk 6 | LABJS_JQueryMobile130 7 | 8 | -------------------------------------------------------------------------------- /src/tabs/LABJS_AngularJS.tab: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | Custom81: Musical Note 6 | LABJS_Angular105 7 | 8 | --------------------------------------------------------------------------------