Brain4it.js 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748
  1. /* Brain4it.js */
  2. if (typeof Brain4it === 'undefined') {
  3. var Brain4it = {}
  4. }
  5. Brain4it.idSequence = 0
  6. /* Reference class */
  7. Brain4it.Reference = function(name) {
  8. this.name = name
  9. }
  10. /* Structure class */
  11. Brain4it.Structure = function(size) {
  12. this.id = String(Brain4it.idSequence++)
  13. this.namesList = []
  14. this.nameToIndexMap = {}
  15. this.shared = false
  16. if (size !== undefined) {
  17. for (var i = 0; i < size; i++) {
  18. this.namesList.push(null)
  19. }
  20. }
  21. }
  22. Brain4it.Structure.prototype = {
  23. size: function() {
  24. return this.namesList.length
  25. },
  26. isShared: function() {
  27. return this.shared
  28. },
  29. setShared: function(shared) {
  30. this.shared = shared
  31. },
  32. add: function() {
  33. this.namesList.push(null)
  34. },
  35. insert: function(index) {
  36. this.shiftIndices(index, 1)
  37. this.namesList.splice(index, 0, null)
  38. },
  39. delete: function(index) {
  40. this.shiftIndices(index, -1)
  41. this.namesList.splice(index, 1)
  42. },
  43. putName: function(index, name) {
  44. if (index === this.namesList.length) {
  45. this.nameToIndexMap[name] = index
  46. this.namesList.push(name)
  47. } else if (index < this.namesList.length) {
  48. var oldName = this.namesList[index]
  49. if (oldName !== null) {
  50. delete this.nameToIndexMap[oldName]
  51. }
  52. if (name !== null) {
  53. var oldIndex = this.nameToIndexMap[name]
  54. this.nameToIndexMap[name] = index
  55. if (oldIndex !== null) {
  56. this.namesList[oldIndex] = null
  57. }
  58. }
  59. this.namesList[index] = name
  60. } else throw 'invalid index'
  61. },
  62. getName: function(index) {
  63. return this.namesList[index]
  64. },
  65. deleteName: function(index) {
  66. this.putName(index, null)
  67. },
  68. getIndex: function(name) {
  69. var index = this.nameToIndexMap[name]
  70. return index === undefined ? -1 : index
  71. },
  72. clone: function() {
  73. var structure = new Brain4it.Structure()
  74. for (var key in this.nameToIndexMap) {
  75. structure.nameToIndexMap[key] = this.nameToIndexMap[key]
  76. }
  77. for (var index = 0; index < this.namesList.length; index++) {
  78. structure.namesList.push(this.namesList[index])
  79. }
  80. return structure
  81. },
  82. shiftIndices: function(index, offset) {
  83. if (offset < 0) {
  84. var name = this.namesList[index]
  85. if (name !== null) {
  86. delete this.nameToIndexMap[name]
  87. }
  88. index++
  89. }
  90. while (index < this.namesList.length) {
  91. var name = this.namesList[index]
  92. if (name !== null) {
  93. this.nameToIndexMap[name] = index + offset
  94. }
  95. index++
  96. }
  97. }
  98. }
  99. /* List class */
  100. Brain4it.List = function() {
  101. this.id = String(Brain4it.idSequence++)
  102. this.elements = []
  103. this.structure = null
  104. }
  105. Brain4it.List.prototype = {
  106. getStructure: function() {
  107. return this.structure
  108. },
  109. setStructure: function(structure) {
  110. structure.setShared(true)
  111. this.structure = structure
  112. if (this.elements.length > structure.size()) {
  113. this.elements.splice(structure.size(), this.elements.length - structure.size())
  114. } else if (this.elements.length < structure.size()) {
  115. for (var i = this.elements.length; i < structure.size(); i++) {
  116. this.elements.push(null)
  117. }
  118. }
  119. },
  120. add: function(element) {
  121. if (this.structure !== null) {
  122. this.modifyStructure()
  123. this.structure.add()
  124. }
  125. this.elements.push(element)
  126. },
  127. insert: function(index, element) {
  128. if (this.structure !== null) {
  129. this.modifyStructure()
  130. this.structure.insert(index)
  131. }
  132. this.elements.splice(index, 0, element)
  133. },
  134. size: function() {
  135. return this.elements.length
  136. },
  137. putByName: function(name, element) {
  138. var old
  139. var index
  140. if (this.structure === null) {
  141. this.structure = new Brain4it.Structure(this.elements.length)
  142. index = -1
  143. } else {
  144. index = this.structure.getIndex(name)
  145. }
  146. if (index === -1) {
  147. this.modifyStructure()
  148. this.structure.putName(this.elements.length, name)
  149. this.elements.push(element)
  150. old = null
  151. } else {
  152. old = this.elements[index]
  153. this.elements[index] = element
  154. }
  155. return old
  156. },
  157. getByName: function(name) {
  158. if (this.structure !== null) {
  159. var index = this.structure.getIndex(name)
  160. if (index !== -1) return this.elements[index]
  161. }
  162. return null
  163. },
  164. removeByName: function(name) {
  165. if (this.structure === null) return null
  166. var index = this.structure.getIndex(name)
  167. if (index === -1) return null
  168. return this.removeByIndex(index)
  169. },
  170. putByIndex: function(index, element) {
  171. var old = this.elements[index]
  172. this.elements[index] = element
  173. return old
  174. },
  175. getByIndex: function(index) {
  176. return this.elements[index]
  177. },
  178. removeByIndex: function(index) {
  179. if (this.structure !== null) {
  180. this.modifyStructure()
  181. this.structure.delete(index)
  182. }
  183. var old = this.elements[index]
  184. this.elements.splice(index, 1)
  185. return old
  186. },
  187. putName: function(index, name) {
  188. if (this.structure === null) {
  189. if (name === null) return
  190. this.structure = new Brain4it.Structure()
  191. } else {
  192. this.modifyStructure()
  193. }
  194. this.structure.putName(index, name)
  195. },
  196. getName: function(index) {
  197. if (this.structure === null) return null
  198. return this.structure.getName(index)
  199. },
  200. has: function(name) {
  201. if (this.structure === null) return false
  202. return this.structure.getIndex(name) !== -1
  203. },
  204. getIndexOfName: function(name) {
  205. if (this.structure === null) return -1
  206. return this.structure.getIndex(name)
  207. },
  208. modifyStructure: function() {
  209. if (this.structure.isShared()) {
  210. this.structure = this.structure.clone()
  211. }
  212. }
  213. }
  214. /* IO classes */
  215. Brain4it.OPEN_LIST_TOKEN = '('
  216. Brain4it.CLOSE_LIST_TOKEN = ')'
  217. Brain4it.NAME_OPERATOR_TOKEN = '=>'
  218. Brain4it.PATH_REFERENCE_SEPARATOR = '/'
  219. Brain4it.DECLARATION_TAG_PREFIX = '<#'
  220. Brain4it.DECLARATION_TAG_SUFFIX = '>'
  221. Brain4it.DECLARATION_TAG_SEPARATOR = ':'
  222. Brain4it.LINK_TAG_PREFIX = '<@'
  223. Brain4it.LINK_TAG_SUFFIX = '>'
  224. /* ServerConstants */
  225. Brain4it.MIMETYPE = 'text/plain'
  226. Brain4it.CHARSET = 'UTF-8'
  227. Brain4it.ACCESS_KEY_HEADER = 'access-key'
  228. Brain4it.MONITOR_HEADER = 'monitor'
  229. Brain4it.SESSION_ID_HEADER = 'session-id'
  230. Brain4it.SERVER_TIME_HEADER = 'server-time'
  231. Brain4it.MODULE_ACCESS_KEY_VAR = 'access-key'
  232. Brain4it.MODULE_METADATA_VAR = 'module-metadata'
  233. Brain4it.DASHBOARDS_FUNCTION_NAME = '@dashboards'
  234. Brain4it.unescapeString = function(text) {
  235. var escape = false
  236. var buffer = ''
  237. for (var i = 0; i < text.length; i++) {
  238. var ch = text.charAt(i)
  239. if (escape) {
  240. switch (ch) {
  241. case 'n':
  242. buffer += '\n'
  243. break
  244. case 'r':
  245. buffer += '\r'
  246. break
  247. case 't':
  248. buffer += '\t'
  249. break
  250. case '\\':
  251. buffer += '\\'
  252. break
  253. case 'b':
  254. buffer += '\b'
  255. break
  256. case 'f':
  257. buffer += '\f'
  258. break
  259. case "'":
  260. buffer += "'"
  261. break
  262. case '"':
  263. buffer += '"'
  264. break
  265. case 'u':
  266. var unicodeBuffer = text.substring(i + 1, i + 5)
  267. buffer += String.fromCharCode(parseInt(unicodeBuffer, 16))
  268. break
  269. default:
  270. throw new 'Invalid character: \\'() + ch
  271. }
  272. escape = false
  273. } else if (ch === '"') {
  274. } else if (ch === '\\') {
  275. escape = true
  276. } else {
  277. buffer += ch
  278. }
  279. }
  280. return buffer
  281. }
  282. Brain4it.escapeString = function(text) {
  283. if (text === null) return 'null'
  284. var buffer = ''
  285. for (var i = 0; i < text.length; i++) {
  286. var ch = text.charAt(i)
  287. if (ch === '\n') buffer += '\\n'
  288. else if (ch === '\r') buffer += '\\r'
  289. else if (ch === '\t') buffer += '\\t'
  290. else if (ch === '\\') buffer += '\\\\'
  291. else if (ch === '"') buffer += '\\"'
  292. else buffer += ch
  293. }
  294. return buffer
  295. }
  296. Brain4it.isNumber = function(value) {
  297. if (typeof value === 'number') {
  298. return true
  299. } else if (typeof value === 'string' && value.length > 0) {
  300. return String(Number(value)) !== 'NaN'
  301. }
  302. }
  303. Brain4it.mayBeNumber = function(value) {
  304. if (typeof value === 'number') {
  305. return true
  306. } else if (typeof value === 'string' && value.length > 0) {
  307. var firstChar = value.charAt(0)
  308. if (firstChar >= '0' && firstChar <= '9') return true
  309. if (value.length === 1) return false
  310. if (firstChar === '.') {
  311. var secondChar = value.charAt(1)
  312. return secondChar >= '0' && secondChar <= '9'
  313. } else if (firstChar === '+' || firstChar === '-') {
  314. var secondChar = value.charAt(1)
  315. if (secondChar >= '0' && secondChar <= '9') return true
  316. if (value.length === 2) return false
  317. if (secondChar === '.') {
  318. var thirdChar = value.charAt(2)
  319. return thirdChar >= '0' && thirdChar <= '9'
  320. }
  321. }
  322. }
  323. return false
  324. }
  325. Brain4it.DECLARATION_TAG_REGEX = new RegExp(Brain4it.DECLARATION_TAG_PREFIX + '..*' + Brain4it.DECLARATION_TAG_SUFFIX)
  326. Brain4it.LINK_TAG_REGEX = new RegExp(Brain4it.LINK_TAG_PREFIX + '..*' + Brain4it.LINK_TAG_SUFFIX)
  327. Brain4it.parseTag = function(text) {
  328. if (Brain4it.LINK_TAG_REGEX.test(text)) {
  329. return new Brain4it.LinkTag(text.substring(Brain4it.LINK_TAG_PREFIX.length, text.length - Brain4it.LINK_TAG_SUFFIX.length))
  330. } else if (Brain4it.DECLARATION_TAG_REGEX.test(text)) {
  331. var index = text.indexOf(Brain4it.DECLARATION_TAG_SEPARATOR, Brain4it.DECLARATION_TAG_PREFIX.length)
  332. if (index === -1) {
  333. return new Brain4it.DeclarationTag(text.substring(Brain4it.DECLARATION_TAG_PREFIX.length, text.length - Brain4it.DECLARATION_TAG_SUFFIX.length))
  334. } else if (index === Brain4it.DECLARATION_TAG_PREFIX.length) {
  335. return new Brain4it.DeclarationTag(null, text.substring(index + 1, text.length - Brain4it.DECLARATION_TAG_SUFFIX.length))
  336. } else {
  337. return new Brain4it.DeclarationTag(text.substring(Brain4it.DECLARATION_TAG_PREFIX.length, index), text.substring(index + 1, text.length - Brain4it.DECLARATION_TAG_SUFFIX.length))
  338. }
  339. }
  340. return null
  341. }
  342. /* DeclarationTag class */
  343. Brain4it.DeclarationTag = function(dataListId, structureListId) {
  344. this.dataListId = dataListId ? String(dataListId) : null
  345. this.structureListId = structureListId ? String(structureListId) : null
  346. }
  347. Brain4it.DeclarationTag.prototype = {
  348. toString: function() {
  349. var buffer = ''
  350. buffer += Brain4it.DECLARATION_TAG_PREFIX
  351. if (this.dataListId !== null) {
  352. buffer += this.dataListId
  353. }
  354. if (this.structureListId !== null && this.structureListId !== this.dataListId) {
  355. buffer += Brain4it.DECLARATION_TAG_SEPARATOR
  356. buffer += this.structureListId
  357. }
  358. buffer += Brain4it.DECLARATION_TAG_SUFFIX
  359. return buffer
  360. }
  361. }
  362. /* LinkTag class */
  363. Brain4it.LinkTag = function(dataListId) {
  364. this.dataListId = dataListId ? String(dataListId) : null
  365. }
  366. Brain4it.LinkTag.prototype = {
  367. toString: function() {
  368. return Brain4it.LINK_TAG_PREFIX + this.dataListId + Brain4it.LINK_TAG_SUFFIX
  369. }
  370. }
  371. /* Token class */
  372. Brain4it.Token = function() {
  373. this.type = null
  374. this.text = null
  375. this.startPosition = 0
  376. this.endPosition = 0
  377. this.object = null
  378. }
  379. Brain4it.Token.EOF = 'EOF'
  380. Brain4it.Token.NULL = 'NULL'
  381. Brain4it.Token.NUMBER = 'NUMBER'
  382. Brain4it.Token.STRING = 'STRING'
  383. Brain4it.Token.BOOLEAN = 'BOOLEAN'
  384. Brain4it.Token.REFERENCE = 'REFERENCE'
  385. Brain4it.Token.OPEN_LIST = 'OPEN_LIST'
  386. Brain4it.Token.CLOSE_LIST = 'CLOSE_LIST'
  387. Brain4it.Token.NAME_OPERATOR = 'NAME_OPERATOR'
  388. Brain4it.Token.TAG = 'TAG'
  389. Brain4it.Token.INVALID = 'INVALID'
  390. Brain4it.Token.prototype = {
  391. length: function() {
  392. return this.endPosition - this.startPosition
  393. }
  394. }
  395. /* Tokenizer class */
  396. Brain4it.Tokenizer = function(stream) {
  397. this.stream = stream
  398. this.charPosition = -1
  399. this.tokens = null // token stack
  400. }
  401. Brain4it.Tokenizer.prototype = {
  402. readToken: function(token) {
  403. if (this.tokens === null || this.tokens.length === 0) {
  404. token = this.readNextToken(token)
  405. } else {
  406. token = this.tokens.pop()
  407. }
  408. return token
  409. },
  410. unreadToken: function(token) {
  411. if (this.tokens === null) {
  412. this.tokens = []
  413. }
  414. this.tokens.push(token)
  415. },
  416. readNextToken: function(token) {
  417. if (token) {
  418. token.type = null
  419. token.text = null
  420. token.object = null
  421. token.startPosition = this.charPosition
  422. } else {
  423. token = new Brain4it.Token()
  424. }
  425. var state = 0
  426. var buffer = ''
  427. var pathBuffer = ''
  428. var end = false
  429. do {
  430. var ch = this.readChar()
  431. switch (state) {
  432. case 0: // Expecting new token
  433. if (ch === null) {
  434. token.startPosition = this.charPosition
  435. token.endPosition = this.charPosition
  436. token.type = Brain4it.Token.EOF
  437. token.text = ''
  438. this.unreadChar()
  439. end = true
  440. } else if (ch === Brain4it.OPEN_LIST_TOKEN) {
  441. token.startPosition = this.charPosition
  442. token.endPosition = this.charPosition + 1
  443. token.type = Brain4it.Token.OPEN_LIST
  444. token.text = Brain4it.OPEN_LIST_TOKEN
  445. end = true
  446. } else if (ch === Brain4it.CLOSE_LIST_TOKEN) {
  447. token.startPosition = this.charPosition
  448. token.endPosition = this.charPosition + 1
  449. token.type = Brain4it.Token.CLOSE_LIST
  450. token.text = Brain4it.CLOSE_LIST_TOKEN
  451. end = true
  452. } else if (this.isSeparator(ch)) {
  453. // skip
  454. } else if (ch === '"') {
  455. // start STRING
  456. buffer += ch
  457. token.startPosition = this.charPosition
  458. state = 1
  459. } else if (ch === Brain4it.PATH_REFERENCE_SEPARATOR) {
  460. pathBuffer += ch
  461. buffer = ''
  462. token.type = Brain4it.Token.REFERENCE
  463. token.startPosition = this.charPosition
  464. token.object = new Brain4it.List()
  465. state = 3
  466. } // start NUMBER, BOOLEAN, NULL or REFERENCE
  467. else {
  468. buffer += ch
  469. token.startPosition = this.charPosition
  470. state = 2
  471. }
  472. break
  473. case 1: // Processing String
  474. if (ch === null) {
  475. // unterminated string
  476. token.endPosition = this.charPosition
  477. token.type = Brain4it.Token.INVALID
  478. try {
  479. token.object = Brain4it.unescapeString(buffer)
  480. } catch (ex) {}
  481. token.text = buffer
  482. end = true
  483. } else if (ch === '\n' || ch === '\r' || ch === '\t') {
  484. token.endPosition = this.charPosition + 1
  485. token.type = Brain4it.Token.INVALID
  486. try {
  487. token.object = Brain4it.unescapeString(buffer)
  488. } catch (ex) {}
  489. token.text = buffer
  490. this.unreadChar()
  491. end = true
  492. } else if (ch === '"' && buffer.charAt(buffer.length - 1) !== '\\') {
  493. buffer += ch
  494. token.endPosition = this.charPosition + 1
  495. token.type = Brain4it.Token.STRING
  496. try {
  497. token.object = Brain4it.unescapeString(buffer)
  498. } catch (ex) {
  499. token.type = Brain4it.Token.INVALID
  500. }
  501. token.text = buffer
  502. end = true
  503. } else {
  504. buffer += ch
  505. }
  506. break
  507. case 2: // Processing token
  508. if (this.isSeparator(ch) || ch === Brain4it.OPEN_LIST_TOKEN || ch === Brain4it.CLOSE_LIST_TOKEN || ch === null) {
  509. if (token.type === Brain4it.Token.INVALID) {
  510. // return INVALID token
  511. } else if (buffer === 'true') {
  512. token.type = Brain4it.Token.BOOLEAN
  513. token.object = true
  514. } else if (buffer === 'false') {
  515. token.type = Brain4it.Token.BOOLEAN
  516. token.object = false
  517. } else if (buffer === 'null') {
  518. token.type = Brain4it.Token.NULL
  519. } else if (buffer === 'NaN') {
  520. token.type = Brain4it.Token.NUMBER
  521. token.object = NaN
  522. } else if (buffer === 'Infinity') {
  523. token.type = Brain4it.Token.NUMBER
  524. token.object = Infinity
  525. } else if (buffer === '-Infinity') {
  526. token.type = Brain4it.Token.NUMBER
  527. token.object = -Infinity
  528. } else if (Brain4it.mayBeNumber(buffer)) {
  529. try {
  530. token.object = Number(buffer)
  531. token.type = Brain4it.Token.NUMBER
  532. } catch (ex) {
  533. token.type = Brain4it.Token.INVALID
  534. }
  535. } else {
  536. var tag = Brain4it.parseTag(buffer)
  537. if (tag !== null) {
  538. token.object = tag
  539. token.type = Brain4it.Token.TAG
  540. } else {
  541. token.type = Brain4it.Token.REFERENCE
  542. }
  543. }
  544. token.text = buffer
  545. token.endPosition = this.charPosition
  546. this.unreadChar()
  547. end = true
  548. } else if (ch === '"') {
  549. buffer += ch
  550. token.type = Brain4it.Token.INVALID
  551. } else if (ch === Brain4it.PATH_REFERENCE_SEPARATOR) {
  552. token.type = Brain4it.Token.REFERENCE
  553. token.object = new Brain4it.List()
  554. this.unreadChar()
  555. state = 3
  556. } else {
  557. // add char to token
  558. buffer += ch
  559. if (buffer === Brain4it.NAME_OPERATOR_TOKEN) {
  560. token.object = null
  561. token.type = Brain4it.Token.NAME_OPERATOR
  562. token.endPosition = this.charPosition + 1
  563. token.text = Brain4it.NAME_OPERATOR_TOKEN
  564. end = true
  565. }
  566. }
  567. break
  568. case 3: // path reference name or index
  569. if (this.isSeparator(ch) || ch === Brain4it.OPEN_LIST_TOKEN || ch === Brain4it.CLOSE_LIST_TOKEN || ch === Brain4it.PATH_REFERENCE_SEPARATOR || ch === null) {
  570. pathBuffer += buffer
  571. if (buffer.length > 0) {
  572. if (buffer[0] >= '0' && buffer[0] <= '9') {
  573. try {
  574. token.object.add(parseInt(buffer))
  575. } catch (ex) {
  576. token.object.add(buffer)
  577. token.type = Brain4it.Token.INVALID
  578. }
  579. } else {
  580. if (buffer[0] === '"' && buffer[buffer.length - 1] === '"') {
  581. try {
  582. token.object.add(Brain4it.unescapeString(buffer))
  583. } catch (ex) {
  584. token.object.add(buffer)
  585. token.type = Brain4it.Token.INVALID
  586. }
  587. } else {
  588. token.object.add(buffer)
  589. if (buffer.indexOf('"') !== -1) {
  590. token.type = Brain4it.Token.INVALID
  591. }
  592. }
  593. }
  594. }
  595. if (ch === Brain4it.PATH_REFERENCE_SEPARATOR) {
  596. pathBuffer += ch
  597. buffer = ''
  598. } else {
  599. token.endPosition = this.charPosition
  600. token.text = pathBuffer
  601. if (token.object instanceof Brain4it.List) {
  602. if (token.object.size() === 0) {
  603. token.object = null
  604. }
  605. }
  606. this.unreadChar(ch)
  607. end = true
  608. }
  609. } else if (ch === '"') {
  610. buffer += ch
  611. state = 4
  612. } else {
  613. buffer += ch
  614. }
  615. break
  616. case 4: // path reference name string
  617. if (ch === null) {
  618. // unterminated string
  619. pathBuffer += buffer
  620. token.endPosition = this.charPosition
  621. token.type = Brain4it.Token.INVALID
  622. token.text = pathBuffer
  623. end = true
  624. } else if (ch === '\n' || ch === '\r' || ch === '\t') {
  625. pathBuffer += buffer
  626. token.endPosition = this.charPosition + 1
  627. token.type = Brain4it.Token.INVALID
  628. token.text = pathBuffer
  629. this.unreadChar(ch)
  630. end = true
  631. } else if (ch === '"' && buffer[buffer.length - 1] !== '\\') {
  632. buffer += ch
  633. state = 3
  634. } else {
  635. buffer += ch
  636. }
  637. default:
  638. }
  639. } while (!end)
  640. return token
  641. },
  642. readChar: function() {
  643. this.charPosition++
  644. if (this.charPosition >= this.stream.length) return null
  645. var ch = this.stream.charAt(this.charPosition)
  646. return ch
  647. },
  648. unreadChar: function() {
  649. if (this.charPosition >= 0) {
  650. this.charPosition--
  651. }
  652. },
  653. isSeparator: function(ch) {
  654. return ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r'
  655. }
  656. }
  657. /* Parser class */
  658. Brain4it.Parser = function() {
  659. this.tokenizer = null
  660. this.listsById = null
  661. this.structuredLists = null
  662. this.stack = null
  663. this.name = null
  664. }
  665. Brain4it.Parser.prototype = {
  666. parse: function(text) {
  667. this.tokenizer = new Brain4it.Tokenizer(text)
  668. this.listsById = {}
  669. this.structuredLists = {}
  670. this.stack = []
  671. this.name = null
  672. var result = null
  673. var token = new Brain4it.Token()
  674. this.tokenizer.readToken(token)
  675. var end = false
  676. do {
  677. if (token.type === Brain4it.Token.INVALID) {
  678. throw 'Invalid token: ' + token.text + ' at ' + token.startPosition
  679. } else if (token.type === Brain4it.Token.OPEN_LIST) {
  680. var list = new Brain4it.List()
  681. if (this.stack.length === 0) {
  682. result = list
  683. } else {
  684. this.addToCurrentList(list)
  685. }
  686. this.stack.push(list)
  687. } else if (token.type === Brain4it.Token.CLOSE_LIST) {
  688. if (this.stack.length > 0) {
  689. this.stack.pop()
  690. if (this.stack.length === 0) {
  691. end = true
  692. }
  693. } else throw 'Unmatched parentheses at ' + token.startPosition
  694. } else if (token.type === Brain4it.Token.NAME_OPERATOR) {
  695. if (this.stack.length === 0) throw 'Missing name at ' + token.startPosition
  696. var currentList = this.stack[this.stack.length - 1]
  697. if (currentList.size() === 0) throw 'Missing name at ' + token.startPosition
  698. var nameObject = currentList.removeByIndex(currentList.size() - 1)
  699. if (nameObject instanceof Brain4it.Reference) {
  700. this.name = nameObject.name
  701. } else {
  702. this.name = String(nameObject)
  703. }
  704. } else if (token.type === Brain4it.Token.TAG) {
  705. if (this.stack.length === 0) {
  706. throw 'Invalid tag at ' + token.startPosition
  707. } else {
  708. var tag = token.object
  709. var currentList = this.stack[this.stack.length - 1]
  710. if (tag instanceof Brain4it.DeclarationTag) {
  711. var dataListId = tag.dataListId
  712. if (dataListId !== null) {
  713. this.listsById[dataListId] = currentList
  714. }
  715. var structureListId = tag.structureListId
  716. if (structureListId !== null) {
  717. var list = this.listsById[structureListId]
  718. if (list === null) {
  719. throw new 'Invalid declaration tag at '() + token.startPosition
  720. }
  721. this.structuredLists[currentList.id] = [currentList, list.getStructure()]
  722. }
  723. } // LinkTag
  724. else {
  725. var dataListId = tag.dataListId
  726. var list = this.listsById[dataListId]
  727. if (list === null) throw 'Invalid link tag at ' + token.startPosition
  728. this.addToCurrentList(list)
  729. }
  730. }
  731. } else if (token.type === Brain4it.Token.REFERENCE) {
  732. var reference = new Brain4it.Reference(token.text)
  733. if (this.stack.length === 0) {
  734. result = reference
  735. end = true
  736. } else {
  737. this.addToCurrentList(reference)
  738. }
  739. } // Literals
  740. else {
  741. var object = token.object
  742. if (this.stack.length === 0) {
  743. result = object
  744. end = true
  745. } // add to current list
  746. else {
  747. this.addToCurrentList(object)
  748. }
  749. }
  750. this.tokenizer.readToken(token)
  751. } while (token.type !== Brain4it.Token.EOF && !end)
  752. if (!(end && token.type === Brain4it.Token.EOF)) throw 'Unmatched parenthesis at ' + token.startPosition
  753. for (var listId in this.structuredLists) {
  754. var pair = this.structuredLists[listId]
  755. var list = pair[0]
  756. var structure = pair[1]
  757. list.setStructure(structure)
  758. }
  759. return result
  760. },
  761. addToCurrentList: function(object) {
  762. var currentList = this.stack[this.stack.length - 1]
  763. if (this.name === null || this.structuredLists[currentList.id]) {
  764. currentList.add(object)
  765. } else {
  766. currentList.putByName(this.name, object)
  767. }
  768. this.name = null
  769. }
  770. }
  771. /* Printer class */
  772. Brain4it.Printer = function() {
  773. this.output = null
  774. this.cursorStack = null
  775. this.dataRegistry = null
  776. this.structureRegistry = null
  777. this.addSpace = true
  778. this.anchorCount = 0
  779. }
  780. Brain4it.Printer.prototype = {
  781. print: function(object) {
  782. this.createAnchorMap(object)
  783. this.cursorStack = []
  784. this.output = []
  785. if (object instanceof Brain4it.List) {
  786. var list = object
  787. this.writeListStart(this.getTag(list))
  788. var cursor = new Brain4it.Printer.Cursor(list)
  789. this.cursorStack.push(cursor)
  790. while (this.cursorStack.length > 0) {
  791. cursor = this.cursorStack.pop()
  792. while (cursor.hasNext()) {
  793. var name = cursor.getName()
  794. var element = cursor.getElement()
  795. cursor.next()
  796. if (this.addSpace) {
  797. this.output.push(' ')
  798. } else {
  799. this.addSpace = true
  800. }
  801. if (name !== null) {
  802. this.writeName(name)
  803. }
  804. if (element instanceof Brain4it.List) {
  805. list = element
  806. var tag = this.getTag(list)
  807. if (tag instanceof Brain4it.LinkTag) {
  808. this.output.push(tag.toString())
  809. } else {
  810. this.cursorStack.push(cursor)
  811. this.writeListStart(tag)
  812. cursor = new Brain4it.Printer.Cursor(list)
  813. }
  814. } else {
  815. this.writeNonList(element)
  816. }
  817. }
  818. this.writeListEnd()
  819. }
  820. } else {
  821. this.writeNonList(object)
  822. }
  823. return this.output.join('')
  824. },
  825. writeName: function(name) {
  826. this.output.push('"')
  827. this.output.push(Brain4it.escapeString(name))
  828. this.output.push('" ')
  829. this.output.push(Brain4it.NAME_OPERATOR_TOKEN)
  830. this.output.push(' ')
  831. },
  832. writeListStart: function(tag) {
  833. this.output.push(Brain4it.OPEN_LIST_TOKEN)
  834. if (tag !== null) {
  835. this.output.push(tag.toString())
  836. } else {
  837. this.addSpace = false
  838. }
  839. },
  840. writeListEnd: function() {
  841. this.output.push(Brain4it.CLOSE_LIST_TOKEN)
  842. },
  843. writeNonList: function(object) {
  844. if (object instanceof Brain4it.Reference) {
  845. this.output.push(object.name)
  846. } else if (typeof object === 'string') {
  847. this.output.push('"')
  848. this.output.push(Brain4it.escapeString(object))
  849. this.output.push('"')
  850. } else {
  851. this.output.push(String(object))
  852. }
  853. },
  854. getTag: function(list) {
  855. var dataAnchor = this.dataRegistry[list.id]
  856. // dataAnchor can't be null because there are no thread issues.
  857. if (dataAnchor.declared) return new Brain4it.LinkTag(dataAnchor.getListId())
  858. var structure = list.getStructure()
  859. var structureAnchor = structure === null ? null : this.structureRegistry[structure.id]
  860. var dataListId = dataAnchor.referenceCount > 1 ? dataAnchor.getListId() : null
  861. var structureListId = structureAnchor !== null && structureAnchor.referenceCount > 1 ? structureAnchor.getListId() : null
  862. if (dataListId === null && structureListId === null) return null
  863. dataAnchor.declared = true
  864. return new Brain4it.DeclarationTag(dataListId, structureListId)
  865. },
  866. createAnchorMap: function(baseObject) {
  867. var list
  868. var cursor
  869. this.dataRegistry = {}
  870. this.structureRegistry = {}
  871. this.cursorStack = []
  872. if (baseObject instanceof Brain4it.List) {
  873. list = baseObject
  874. this.registerList(list)
  875. cursor = new Brain4it.Printer.Cursor(list)
  876. this.cursorStack.push(cursor)
  877. while (this.cursorStack.length > 0) {
  878. cursor = this.cursorStack.pop()
  879. while (cursor.hasNext()) {
  880. var element = cursor.getElement()
  881. cursor.next()
  882. if (element instanceof Brain4it.List) {
  883. list = element
  884. if (this.registerList(list)) {
  885. this.cursorStack.push(cursor)
  886. cursor = new Brain4it.Printer.Cursor(list)
  887. }
  888. }
  889. }
  890. }
  891. }
  892. },
  893. registerList: function(list) {
  894. var dataAnchor = this.dataRegistry[list.id]
  895. if (dataAnchor === undefined) {
  896. dataAnchor = new Brain4it.Printer.Anchor(this)
  897. this.dataRegistry[list.id] = dataAnchor
  898. var structure = list.getStructure()
  899. if (structure !== null) {
  900. var structureAnchor = this.structureRegistry[structure.id]
  901. if (structureAnchor === undefined) {
  902. this.structureRegistry[structure.id] = dataAnchor
  903. } else {
  904. structureAnchor.referenceCount++
  905. }
  906. }
  907. return true
  908. } else {
  909. dataAnchor.referenceCount++
  910. return false
  911. }
  912. }
  913. }
  914. Brain4it.Printer.Anchor = function(printer) {
  915. this.printer = printer
  916. this.listId = 0
  917. this.referenceCount = 1
  918. this.declared = false
  919. }
  920. Brain4it.Printer.Anchor.prototype = {
  921. getListId: function() {
  922. if (this.listId === 0) {
  923. this.listId = String(++this.printer.anchorCount)
  924. }
  925. return this.listId
  926. },
  927. toString: function() {
  928. return this.listId + '/' + this.referenceCount
  929. }
  930. }
  931. Brain4it.Printer.Cursor = function(list) {
  932. this.list = list
  933. this.index = 0
  934. }
  935. Brain4it.Printer.Cursor.prototype = {
  936. hasNext: function() {
  937. return this.index < this.list.size()
  938. },
  939. next: function() {
  940. this.index++
  941. },
  942. getName: function() {
  943. return this.list.getName(this.index)
  944. },
  945. getElement: function() {
  946. return this.list.getByIndex(this.index)
  947. }
  948. }
  949. /* Formatter class */
  950. Brain4it.Formatter = function(configuration) {
  951. this.configuration = configuration || new Brain4it.Formatter.Configuration()
  952. this.output = null
  953. this.tokenizer = null
  954. this.baseList = null
  955. this.currentList = null
  956. this.indentLevel = 0
  957. this.written = false
  958. this.name = null
  959. }
  960. Brain4it.Formatter.prototype = {
  961. format: function(code) {
  962. this.output = []
  963. this.tokenizer = new Brain4it.Tokenizer(code)
  964. this.baseList = null
  965. this.currentList = null
  966. this.indentLevel = 0
  967. this.written = false
  968. this.name = null
  969. var inline = false
  970. var token
  971. var end = false
  972. while (!end) {
  973. token = this.tokenizer.readToken()
  974. if (token.type === Brain4it.Token.EOF) {
  975. if (this.baseList !== null) {
  976. this.baseList.print()
  977. }
  978. end = true
  979. } else if (inline) {
  980. this.addToken(token)
  981. if (this.currentList === null) {
  982. // baseList closed
  983. if (this.name === null) {
  984. this.nextLine()
  985. } else {
  986. this.printSpace()
  987. }
  988. this.baseList.print()
  989. this.baseList = null
  990. this.name = null
  991. inline = false
  992. } else if (this.isBreakRequired()) {
  993. if (this.name === null || this.indentLevel * this.configuration.indentSize + this.baseList.getLength() > this.configuration.maxColumns) {
  994. this.baseList.releaseExcedent()
  995. this.nextLine()
  996. this.baseList.print()
  997. this.baseList = null
  998. this.currentList = null
  999. this.indentLevel++
  1000. inline = false
  1001. }
  1002. // else try to print baseList inline in the next line
  1003. this.name = null
  1004. }
  1005. } // !inline: tokens in vertical
  1006. else {
  1007. if (token.type === Brain4it.Token.OPEN_LIST) {
  1008. this.baseList = new Brain4it.Formatter.TokenList(this, token)
  1009. this.currentList = this.baseList
  1010. inline = true
  1011. } else if (token.type === Brain4it.Token.CLOSE_LIST) {
  1012. this.indentLevel--
  1013. this.nextLine()
  1014. this.printToken(token)
  1015. } else {
  1016. this.nextLine()
  1017. this.printToken(token)
  1018. var nextToken = this.tokenizer.readToken()
  1019. if (nextToken.type === Brain4it.Token.NAME_OPERATOR) {
  1020. this.name = String(token.text)
  1021. this.printSpace()
  1022. this.printToken(nextToken) // name operator
  1023. var nextNextToken = this.tokenizer.readToken()
  1024. if (nextNextToken.type !== Brain4it.Token.OPEN_LIST && nextNextToken.type !== Brain4it.Token.EOF) {
  1025. if (this.getCurrentColumn() + 1 + nextNextToken.length() > this.configuration.maxColumns) {
  1026. this.nextLine()
  1027. } else {
  1028. this.printSpace()
  1029. }
  1030. this.printToken(nextNextToken)
  1031. this.name = null
  1032. } else this.tokenizer.unreadToken(nextNextToken)
  1033. } else this.tokenizer.unreadToken(nextToken)
  1034. }
  1035. }
  1036. }
  1037. return this.output.join('')
  1038. },
  1039. addToken: function(token) {
  1040. if (token.type === Brain4it.Token.OPEN_LIST) {
  1041. var tokenList = new Brain4it.Formatter.TokenList(this, token, this.currentList)
  1042. this.currentList.add(tokenList)
  1043. this.currentList = tokenList
  1044. } else if (token.type === Brain4it.Token.CLOSE_LIST) {
  1045. this.currentList.add(token)
  1046. this.currentList = this.currentList.parent
  1047. } else {
  1048. this.currentList.add(token)
  1049. }
  1050. },
  1051. getCurrentColumn: function() {
  1052. var column = this.indentLevel * this.configuration.indentSize
  1053. if (this.name !== null) column += this.name.length + Brain4it.NAME_OPERATOR_TOKEN.length + 2
  1054. return column
  1055. },
  1056. isBreakRequired: function() {
  1057. var configuration = this.configuration
  1058. // check is maxColumn is exceeded
  1059. if (this.getCurrentColumn() + this.baseList.getLength() > configuration.maxColumns) return true
  1060. // check if function is not inline
  1061. var functionName = this.baseList.getFunctionName()
  1062. if (functionName === null) return false
  1063. if (configuration.notInlineFunctions.indexOf(functionName) === -1) return false
  1064. var args = configuration.inlineArguments[functionName]
  1065. if (args === undefined) return true
  1066. return this.baseList.getCompletedArguments() >= args
  1067. },
  1068. nextLine: function() {
  1069. if (this.written) {
  1070. this.printCR()
  1071. for (var i = 0; i < this.indentLevel; i++) {
  1072. this.printIndent(this.configuration.indentSize)
  1073. }
  1074. }
  1075. },
  1076. printToken: function(token) {
  1077. this.output.push(token.text)
  1078. this.written = true
  1079. },
  1080. printCR: function() {
  1081. this.output.push('\n')
  1082. },
  1083. printIndent: function(indentSize) {
  1084. for (var i = 0; i < indentSize; i++) {
  1085. this.output.push(' ')
  1086. }
  1087. },
  1088. printSpace: function() {
  1089. this.output.push(' ')
  1090. }
  1091. }
  1092. Brain4it.Formatter.TokenList = function(formatter, openToken, parent) {
  1093. this.formatter = formatter
  1094. this.elements = [openToken]
  1095. this.parent = parent || null
  1096. }
  1097. Brain4it.Formatter.TokenList.prototype = {
  1098. releaseExcedent: function() {
  1099. var configuration = this.formatter.configuration
  1100. var functionName = this.getFunctionName()
  1101. if (functionName === null) {
  1102. this.unreadTokens(1)
  1103. } else {
  1104. var args = configuration.inlineArguments[functionName]
  1105. if (args === undefined) {
  1106. this.unreadTokens(2)
  1107. } else {
  1108. if (this.getCompletedArguments() >= args) {
  1109. var count = args + 2
  1110. if (this.getLength(count) < configuration.maxColumns) {
  1111. this.unreadTokens(count)
  1112. } else {
  1113. this.unreadTokens(2)
  1114. }
  1115. } else {
  1116. this.unreadTokens(2)
  1117. }
  1118. }
  1119. }
  1120. },
  1121. add: function(elem) {
  1122. this.elements.push(elem)
  1123. },
  1124. unreadTokens: function(tokensLeft) {
  1125. for (var i = this.elements.length - 1; i >= tokensLeft; i--) {
  1126. var elem = this.elements[i]
  1127. if (elem instanceof Brain4it.Token) {
  1128. var token = elem
  1129. this.formatter.tokenizer.unreadToken(token)
  1130. } // TokenList
  1131. else {
  1132. var tokenList = elem
  1133. tokenList.unreadTokens(0)
  1134. }
  1135. this.elements.pop()
  1136. }
  1137. },
  1138. isClosed: function() {
  1139. var size = this.elements.length
  1140. var last = this.elements[size - 1]
  1141. if (last instanceof Brain4it.Token) {
  1142. var token = last
  1143. return token.type === Brain4it.Token.CLOSE_LIST
  1144. }
  1145. return false
  1146. },
  1147. getCompletedArguments: function() {
  1148. var size = this.elements.length
  1149. var last = this.elements[size - 1]
  1150. if (last instanceof Brain4it.Token) {
  1151. var token = last
  1152. return token.type === Brain4it.Token.CLOSE_LIST ? size - 3 : size - 2
  1153. } // TokenList
  1154. else {
  1155. var tokenList = last
  1156. return tokenList.isClosed() ? size - 2 : size - 3
  1157. }
  1158. },
  1159. getLength: function(count) {
  1160. count = count || this.elements.length
  1161. var length = 0
  1162. for (var i = 0; i < count; i++) {
  1163. var elem = this.elements[i]
  1164. if (i > 1 && !(elem instanceof Brain4it.Token && elem.type === Brain4it.Token.CLOSE_LIST)) {
  1165. length++ // space
  1166. }
  1167. if (elem instanceof Brain4it.Token) {
  1168. var token = elem
  1169. length += token.length()
  1170. } // TokenList
  1171. else {
  1172. var tokenList = elem
  1173. length += tokenList.getLength()
  1174. }
  1175. }
  1176. return length
  1177. },
  1178. print: function() {
  1179. for (var i = 0; i < this.elements.length; i++) {
  1180. var elem = this.elements[i]
  1181. if (i > 1 && !(elem instanceof Brain4it.Token && elem.type === Brain4it.Token.CLOSE_LIST)) {
  1182. this.formatter.printSpace()
  1183. }
  1184. if (elem instanceof Brain4it.Token) {
  1185. var token = elem
  1186. this.formatter.printToken(token)
  1187. } // TokenList
  1188. else {
  1189. var tokenList = elem
  1190. tokenList.print()
  1191. }
  1192. }
  1193. },
  1194. getFunctionName: function() {
  1195. if (this.elements.length < 2) return null
  1196. var elem = this.elements[1]
  1197. if (!(elem instanceof Brain4it.Token)) return null
  1198. var token = elem
  1199. if (token.type !== Brain4it.Token.REFERENCE) return null
  1200. if (this.elements.length >= 3) {
  1201. // test reference is not a name
  1202. elem = this.elements[2]
  1203. if (elem instanceof Brain4it.Token) {
  1204. var token2 = elem
  1205. if (token2.type === Brain4it.Token.NAME_OPERATOR) return null
  1206. }
  1207. }
  1208. return token.text
  1209. }
  1210. }
  1211. Brain4it.Formatter.Configuration = function(indentSize, maxColumns, notInlineFunctions, inlineArguments) {
  1212. this.indentSize = indentSize || 2
  1213. this.maxColumns = maxColumns || 60
  1214. this.notInlineFunctions = notInlineFunctions || ['do', 'cond', 'for', 'when', 'while']
  1215. this.inlineArguments = inlineArguments || {
  1216. function: 1,
  1217. if: 1,
  1218. set: 1,
  1219. when: 1,
  1220. while: 1,
  1221. for: 3,
  1222. 'for-each': 2,
  1223. apply: 2,
  1224. find: 2,
  1225. sync: 1
  1226. }
  1227. }
  1228. /* Client class */
  1229. Brain4it.Client = function(serverUrl, path, accessKey, sessionId) {
  1230. this.serverUrl = serverUrl
  1231. this.path = path
  1232. this.accessKey = accessKey || null
  1233. this.sessionId = sessionId || null
  1234. this.method = 'POST'
  1235. this.request = new XMLHttpRequest()
  1236. this.callback = null
  1237. }
  1238. Brain4it.Client.prototype = {
  1239. send: function(data) {
  1240. var scope = this
  1241. var request = this.request
  1242. if (request.readyState > 0) request.abort()
  1243. var path = this.path === null ? '' : '/' + escape(this.path)
  1244. request.open(this.method, this.serverUrl + path, true)
  1245. if (this.accessKey) {
  1246. request.setRequestHeader(Brain4it.ACCESS_KEY_HEADER, this.accessKey)
  1247. }
  1248. if (this.sessionId) {
  1249. request.setRequestHeader(Brain4it.SESSION_ID_HEADER, this.sessionId)
  1250. }
  1251. if (data) {
  1252. request.setRequestHeader('Content-Type', Brain4it.MIMETYPE + '; charset=' + Brain4it.CHARSET)
  1253. }
  1254. request.onreadystatechange = function() {
  1255. if (request.readyState === 4) {
  1256. if (scope.callback) {
  1257. var value = request.getResponseHeader(Brain4it.SERVER_TIME_HEADER)
  1258. var serverTime = value === null ? 0 : parseFloat(value)
  1259. scope.callback(request.status, request.responseText, serverTime)
  1260. }
  1261. }
  1262. }
  1263. request.send(data)
  1264. },
  1265. abort: function() {
  1266. if (this.request) {
  1267. this.request.abort()
  1268. }
  1269. }
  1270. }
  1271. /* Monitor class */
  1272. Brain4it.Monitor = function(serverUrl, module, accessKey, sessionId) {
  1273. this.url = serverUrl + '/' + escape(module)
  1274. this.accessKey = accessKey
  1275. this.sessionId = sessionId || null
  1276. this.listeners = {}
  1277. this.watchRequest = new XMLHttpRequest()
  1278. this.connectionDelay = 100
  1279. this.pollingInterval = 0
  1280. this.timerId = null
  1281. this.monitorSessionId = null
  1282. }
  1283. Brain4it.Monitor.prototype = {
  1284. watch: function(functionName, listener) {
  1285. if (functionName === undefined || functionName === null) return
  1286. var functionListeners = this.listeners[functionName]
  1287. if (functionListeners === undefined) {
  1288. functionListeners = [listener]
  1289. this.listeners[functionName] = functionListeners
  1290. } else {
  1291. if (functionListeners.indexOf(listener) === -1) {
  1292. functionListeners.push(listener)
  1293. }
  1294. }
  1295. var scope = this
  1296. if (this.timerId !== null) {
  1297. // delayed start
  1298. clearTimeout(this.timerId)
  1299. }
  1300. this.timerId = setTimeout(function() {
  1301. scope.sendWatchRequest()
  1302. }, this.connectionDelay)
  1303. },
  1304. unwatch: function(functionName, listener) {
  1305. var functionListeners = this.listeners[functionName]
  1306. if (functionListeners) {
  1307. var index = functionListeners.indexOf(listener)
  1308. if (index !== -1) {
  1309. functionListeners.splice(index, 1)
  1310. if (functionListeners.length === 0) {
  1311. delete this.listeners[functionName]
  1312. }
  1313. }
  1314. }
  1315. this.sendWatchRequest()
  1316. },
  1317. unwatchAll: function(sync) {
  1318. this.listeners = {}
  1319. var watchRequest = this.watchRequest
  1320. if (watchRequest.readyState !== 0 && watchRequest.readyState !== 4) {
  1321. // request in progress
  1322. watchRequest.abort()
  1323. this.sendUnwatchRequest(sync)
  1324. }
  1325. },
  1326. sendWatchRequest: function() {
  1327. var scope = this
  1328. var watchRequest = this.watchRequest
  1329. if (watchRequest.readyState !== 0 && watchRequest.readyState !== 4) {
  1330. // cancel request in progress
  1331. watchRequest.abort()
  1332. this.sendUnwatchRequest()
  1333. }
  1334. var list = new Brain4it.List()
  1335. for (var functionName in this.listeners) {
  1336. list.add(functionName)
  1337. }
  1338. if (list.size() === 0) return // Monitor is idle, so exit
  1339. watchRequest.open('POST', this.url, true)
  1340. watchRequest.setRequestHeader('Content-Type', Brain4it.MIMETYPE + '; charset=' + Brain4it.CHARSET)
  1341. var pollingInterval = 0
  1342. if (typeof this.pollingInterval === 'number') {
  1343. if (this.pollingInterval > 0) {
  1344. pollingInterval = this.pollingInterval
  1345. }
  1346. }
  1347. watchRequest.setRequestHeader(Brain4it.MONITOR_HEADER, String(pollingInterval))
  1348. if (this.accessKey) {
  1349. watchRequest.setRequestHeader(Brain4it.ACCESS_KEY_HEADER, this.accessKey)
  1350. }
  1351. if (this.sessionId) {
  1352. watchRequest.setRequestHeader(Brain4it.SESSION_ID_HEADER, this.sessionId)
  1353. }
  1354. var index = 0
  1355. watchRequest.onprogress = function() {
  1356. var response = watchRequest.response
  1357. var chunkEnd = response.indexOf('\n', index)
  1358. while (chunkEnd !== -1) {
  1359. var chunk = response.substring(index, chunkEnd)
  1360. scope.processChunk(chunk)
  1361. index = chunkEnd + 1
  1362. chunkEnd = response.indexOf('\n', index)
  1363. }
  1364. }
  1365. watchRequest.onreadystatechange = function(event) {
  1366. if (watchRequest.readyState === 4) {
  1367. console.info('request terminated')
  1368. scope.sendWatchRequest()
  1369. }
  1370. }
  1371. var printer = new Brain4it.Printer()
  1372. var functionNames = printer.print(list)
  1373. console.info(functionNames)
  1374. watchRequest.send(functionNames)
  1375. },
  1376. sendUnwatchRequest: function(sync) {
  1377. if (this.monitorSessionId) {
  1378. if (typeof sync === 'undefined') sync = false
  1379. var unwatchRequest = new XMLHttpRequest()
  1380. unwatchRequest.open('POST', this.url, !sync)
  1381. unwatchRequest.setRequestHeader('Content-Type', Brain4it.MIMETYPE + '; charset=' + Brain4it.CHARSET)
  1382. unwatchRequest.setRequestHeader(Brain4it.MONITOR_HEADER, '0')
  1383. unwatchRequest.send('"' + this.monitorSessionId + '"')
  1384. console.info('unwatch request sent ' + this.monitorSessionId + ' ' + sync)
  1385. this.monitorSessionId = null
  1386. }
  1387. },
  1388. processChunk: function(chunk) {
  1389. try {
  1390. var parser = new Brain4it.Parser()
  1391. var change = parser.parse(chunk)
  1392. if (change instanceof Brain4it.List) {
  1393. var functionName = change.getByIndex(0)
  1394. var value = change.getByIndex(1)
  1395. var serverTime = change.size() < 3 ? 0 : change.getByIndex(2)
  1396. if (typeof serverTime !== 'number') serverTime = 0
  1397. var functionListeners = this.listeners[functionName]
  1398. if (functionListeners) {
  1399. for (var i = 0; i < functionListeners.length; i++) {
  1400. functionListeners[i](functionName, value, serverTime)
  1401. }
  1402. }
  1403. } else if (typeof change === 'string') {
  1404. this.monitorSessionId = change
  1405. }
  1406. } catch (ex) {
  1407. // Ignore
  1408. }
  1409. }
  1410. }
  1411. /* Invoker class */
  1412. Brain4it.Invoker = function(client, moduleName) {
  1413. this.client = client
  1414. this.moduleName = moduleName
  1415. this.queue = []
  1416. this.sending = false
  1417. }
  1418. Brain4it.Invoker.prototype = {
  1419. invoke: function(functionName, value, coalesce, callback) {
  1420. if (typeof functionName !== 'string') throw 'functionName is required'
  1421. var found = false
  1422. if (coalesce && callback !== null) {
  1423. // update value of call from the same callback
  1424. var i = 0
  1425. while (i < this.queue.length && !found) {
  1426. var call = this.queue[i]
  1427. if (callback === call.callback) {
  1428. call.value = value // set next value to send
  1429. found = true
  1430. }
  1431. i++
  1432. }
  1433. }
  1434. if (!found) {
  1435. // add new call to queue
  1436. var call = {
  1437. functionName: functionName,
  1438. value: value,
  1439. callback: callback
  1440. }
  1441. this.queue.push(call)
  1442. }
  1443. this.internalInvoke()
  1444. return !found
  1445. },
  1446. internalInvoke: function() {
  1447. if (this.sending) return
  1448. var call = this.queue.shift()
  1449. if (call) {
  1450. var functionName = call.functionName
  1451. var value = call.value
  1452. var valueString
  1453. if (typeof value === 'string') {
  1454. valueString = '"' + Brain4it.escapeString(value) + '"'
  1455. } else {
  1456. var printer = new Brain4it.Printer()
  1457. valueString = printer.print(value)
  1458. }
  1459. var client = this.client
  1460. var scope = this
  1461. client.method = 'POST'
  1462. client.path = this.moduleName + '/' + functionName
  1463. client.callback = function(status, resultString, serverTime) {
  1464. if (call.callback) {
  1465. call.callback(status, resultString, serverTime)
  1466. }
  1467. scope.sending = false
  1468. scope.internalInvoke()
  1469. }
  1470. scope.sending = true
  1471. client.send(valueString)
  1472. }
  1473. }
  1474. }
  1475. Brain4it.FunctionInvoker = function(invoker, functionName, invokeInterval) {
  1476. this.invoker = invoker
  1477. this.functionName = functionName
  1478. this.invokeInterval = invokeInterval || 0
  1479. this.invokeTime = 0
  1480. this.sending = 0
  1481. this.nextValue = null
  1482. this.timerId = null
  1483. var scope = this
  1484. this.callback = function(status, resultString, serverTime) {
  1485. scope.sending--
  1486. scope.invokeTime = serverTime
  1487. }
  1488. }
  1489. Brain4it.FunctionInvoker.prototype = {
  1490. invoke: function(value) {
  1491. if (this.invokeInterval === 0) {
  1492. if (this.invoker.invoke(this.functionName, value, true, this.callback)) {
  1493. this.sending++
  1494. }
  1495. } else {
  1496. this.nextValue = value
  1497. this.internalInvoke()
  1498. }
  1499. },
  1500. isSending: function() {
  1501. return this.sending > 0 || this.nextValue !== null
  1502. },
  1503. updateInvokeTime: function(serverTime) {
  1504. if (serverTime > this.invokeTime) {
  1505. this.invokeTime = serverTime
  1506. return true
  1507. }
  1508. return false
  1509. },
  1510. internalInvoke: function() {
  1511. if (this.timerId) return // internalInvoke will be called soon by timer
  1512. if (this.nextValue === null) return
  1513. var value = this.nextValue
  1514. this.nextValue = null
  1515. if (this.invoker.invoke(this.functionName, value, true, this.callback)) {
  1516. this.sending++
  1517. }
  1518. var scope = this
  1519. this.timerId = setTimeout(function() {
  1520. scope.timerId = null
  1521. scope.internalInvoke()
  1522. }, this.invokeInterval)
  1523. }
  1524. }
  1525. /* Helper class */
  1526. Brain4it.Helper = function(serverUrl, module, accessKey) {
  1527. this.functions = []
  1528. this.functionsMap = {}
  1529. this.serverUrl = serverUrl
  1530. this.module = module
  1531. this.accessKey = accessKey
  1532. }
  1533. Brain4it.Helper.prototype = {
  1534. LOAD_FUNCTIONS: '(functions)',
  1535. loadFunctions: function(callback) {
  1536. var scope = this
  1537. var client = new Brain4it.Client(this.serverUrl, this.module, this.accessKey)
  1538. client.method = 'POST'
  1539. client.callback = function(status, output) {
  1540. if (status === 200) {
  1541. var parser = new Brain4it.Parser()
  1542. var object = parser.parse(output)
  1543. if (object instanceof Brain4it.List) {
  1544. scope.functions = []
  1545. scope.functionsMap = {}
  1546. for (var i = 0; i < object.size(); i++) {
  1547. var functionReference = object.getByIndex(i)
  1548. var functionName = functionReference.name
  1549. scope.functions.push(functionName)
  1550. scope.functionsMap[functionName] = true
  1551. }
  1552. }
  1553. }
  1554. if (callback) {
  1555. callback(scope.functions, output)
  1556. }
  1557. }
  1558. client.send(this.LOAD_FUNCTIONS)
  1559. },
  1560. isFunction: function(name) {
  1561. return this.functionsMap.propertyIsEnumerable(name)
  1562. }
  1563. }
  1564. export { Brain4it }