buildDom.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. (function (global, p5VendorJs) {
  2. if (!p5VendorJs.React) throw "Missing React"
  3. if (!p5VendorJs.ReactDOM) throw "Missing ReactDOM"
  4. if (!p5VendorJs.createReactClass) throw "Missing createReactClass"
  5. var React = p5VendorJs.React
  6. var ReactDOM = p5VendorJs.ReactDOM
  7. var createReactClass = p5VendorJs.createReactClass
  8. var h = React.createElement
  9. var DBG = 0;
  10. var DBG1 = 1;
  11. // global.p5VendorJs['P5UI__MyWidget'] = createReactClass({
  12. // componentWillMount: function () { console.log('MyWidget::componentWillMount...'); },
  13. // render: function () {
  14. // console.log('MyWidget::render... state, props', this.state, this.props);
  15. // var defaultProps = {}
  16. // return h('div', Object.assign(defaultProps, {
  17. // ref: function (node) { this.rootNode = node; }.bind(this),
  18. // // onSubmit: this.submit.bind(this)
  19. // }), this.props.children)
  20. // },
  21. // componentDidMount: function () { console.log('MyWidget::componentDidMount...'); },
  22. // componentWillReceiveProps: function (nextProps) { console.log('MyWidget::componentWillReceiveProps(nextProps)...', nextProps); },
  23. // shouldComponentUpdate: function (nextProps, nextState) { console.log('MyWidget::shouldComponentUpdate(nextProps, nextState)...', nextProps, nextState); },
  24. // componentWillUpdate: function (nextProps, nextState) { console.log('MyWidget::componentWillUpdate(nextProps, nextState)...', nextProps, nextState); },
  25. // componentDidUpdate: function (prevProps, prevState) { console.log('MyWidget::componentDidUpdate(prevProps, prevState)...', prevProps, prevState); },
  26. // componentWillUnmount: function () { console.log('MyWidget::componentWillUnmount...'); },
  27. // })
  28. var P5UI__RawHtml = createReactClass({
  29. componentDidMount: function () {
  30. if (this.props.rawHtml) {
  31. this.rootNode.innerHTML = this.props.rawHtml
  32. }
  33. },
  34. render: function () {
  35. return h('div', {
  36. ref: function (node) { this.rootNode = node; }.bind(this),
  37. })
  38. }
  39. })
  40. global.p5VendorJs['P5UI__RawHtml'] = P5UI__RawHtml
  41. var P5UI__FeatureEditForm = createReactClass({
  42. componentDidMount: function () {
  43. if(DBG)console.warn('P5UI__FeatureEditForm::componentDidMount this.rootNode', this.rootNode)
  44. jQuery(this.rootNode).find('textarea').autosize();
  45. jQuery(this.rootNode).find('.frm-help').popover({trigger:'hover'});
  46. jQuery(this.rootNode).find('.show-last-value input').on('input', function(e) {
  47. var input, btn, btnIco;
  48. input = jQuery(e.target);
  49. btn = input.next('.button-appendBack');
  50. btnIco = btn.find('.glyphicon');
  51. if (btn.attr('title') != input.val()) {
  52. btnIco.show();
  53. } else {
  54. btnIco.hide();
  55. }
  56. });
  57. jQuery(this.rootNode).find('.show-last-value').find('.button-appendBack').on('click', function(e) {
  58. var input, btn, btnIco;
  59. btn = jQuery(this);
  60. btnIco = btn.find('.glyphicon');
  61. input = btn.prev();
  62. if (input.is('input')) {
  63. if (btn.attr('title') != input.val()) {
  64. input.val(btn.attr('title'));
  65. btnIco.hide();
  66. }
  67. }
  68. });
  69. },
  70. submit: function (e) {
  71. if(DBG)console.warn('P5UI__FeatureEditForm::submit this.rootNode', this.rootNode)
  72. e.preventDefault()
  73. var formData = {};
  74. jQuery(this.rootNode).serializeArray().map(function(i) { formData[i.name] = i.value; });// TODO: edit Widget - send only updated fields
  75. // TODO: change Edit btn to return to edit record with fields -> show form
  76. var taskCont = jQuery(this.rootNode).parents('.AjaxTableTaskCnt').parent(); // jQuery('#{$this->_htmlID}_task').parent();
  77. //taskCont.empty();
  78. taskCont.children().fadeOut('slow');
  79. var alertCntWrap = jQuery('<div class="AjaxTableAlert AjaxTable-loading"></div>').prependTo(taskCont)
  80. , alertCnt = jQuery('<div class="container"></div>').prependTo(alertCntWrap);
  81. jQuery('<div class="alert alert-info"><div style="padding:0 0 0 20px; background:url(./icon/loading.gif) no-repeat left top;"> zapisywanie ... </div></div>').appendTo(alertCnt);
  82. function notifyAjaxCallback(data) {
  83. var notify = {}, outMsg = '';
  84. notify.type = (data && data.type)? data.type : '';
  85. notify.msg = (data && data.msg)? data.msg : '';
  86. switch (notify.type) {
  87. case 'success':
  88. if (!notify.msg) notify.msg = 'Dane poprawnie zaktualizowane';
  89. break;
  90. case 'info':
  91. if (!notify.msg) notify.msg = 'Nie wprowadzono żadnych zmian';
  92. break;
  93. case 'error':
  94. if (!notify.msg) notify.msg = 'Wystąpiły błędy';
  95. break;
  96. case 'warning':
  97. notify.type = 'warn';
  98. if (!notify.msg) notify.msg = 'Wystąpiły błędy';
  99. break;
  100. default:
  101. notify.msg = 'Nieznany błąd';
  102. if (data && data.errorCode) notify.msg += ' ' + data.errorCode;
  103. notify.type = '';
  104. }
  105. jQuery.notify(notify.msg, notify.type);
  106. var alertType = ('error' == data.type) ? 'danger' : data.type;
  107. outMsg = '<div class="alert alert-' + alertType + '">' + notify.msg + '</div>';
  108. return outMsg;
  109. }
  110. var idRecord = this.props.idRecord
  111. var tableLabelHtml = this.props.tableLabelHtml
  112. superagent
  113. .post(this.props.ajaxSaveUrl)
  114. .type('json') // header ĺapplication/x-www-form-urlencoded' requires type('form');
  115. .send({
  116. namespace: this.props.namespace,
  117. primaryKey: this.props.idRecord,
  118. form: formData
  119. })
  120. .set('Accept', 'application/json')
  121. .end(function(err, res) {
  122. var payload;
  123. if (err || !res.ok || 'application/json' !== res.type) {
  124. payload = {type: 'warning', msg: res.body.msg || 'Wystąpiły błędy', body: res.body};
  125. } else {
  126. payload = {type: 'success', msg: res.body.msg || '', body: res.body};
  127. }
  128. var data = res.body;
  129. alertCntWrap.removeClass('AjaxTable-loading');
  130. alertCnt.empty();
  131. if (false === ['success', 'info'].indexOf(payload.type)) {
  132. jQuery(errorTxt).appendTo(alertCnt);
  133. var errLinks = jQuery('<div class="breadcrumb"></div>').appendTo(alertCnt);
  134. jQuery('<a href="#" onclick="return tableAjaxBackToTable();"> <i class="icon-arrow-left"></i> Wróć do tabeli '+tableLabelHtml+'</a>').appendTo(errLinks);
  135. var backToEditBtn = jQuery('<a href="#EDIT/'+idRecord+'/' + Math.random(1).toString().substr(2) + '" class="btn btn-link btn-small"> <i class="icon-pencil"></i> Popraw dane '+idRecord+'</a>').appendTo(errLinks);
  136. backToEditBtn.on('click', function(){
  137. alertCnt.remove();
  138. taskCont.children().fadeIn('slow');
  139. return false;
  140. });
  141. } else {
  142. var outMsg = notifyAjaxCallback(payload);
  143. var out = '';
  144. out += outMsg;
  145. out += '<div class="breadcrumb">' +
  146. ' <a href="#" onclick="return tableAjaxBackToTable();" class="btn btn-link btn-sm"> <i class="glyphicon glyphicon-arrow-left"></i> Wróć do tabeli '+tableLabelHtml+'</a>' +
  147. ' <a href="#EDIT/'+idRecord+'/' + Math.random(1).toString().substr(2) + '" class="btn btn-link btn-sm"> <i class="glyphicon glyphicon-pencil"></i> Edytuj rekord '+idRecord+'</a>' +
  148. '</div>';
  149. jQuery(out).appendTo(alertCnt);
  150. // add rowFunctions from response
  151. if (data && data.rowFunctions && data.primaryKey) {
  152. var rowFunWrapNode = $('<div class="container"></div>').insertAfter(alertCnt);
  153. var rowFunListNode = $('<ul></ul>').appendTo(rowFunWrapNode);
  154. var keys = Object.keys(data.rowFunctions),
  155. total = keys.length,
  156. moreFuncBtnNode,
  157. moreFunctions = [],
  158. idx
  159. ;
  160. moreFunctions = keys.splice(3);
  161. keys.forEach(function(key) {
  162. var cellNode = $('<li></li>');
  163. var funObj = data.rowFunctions[key],
  164. funcNode = p5UI_TableAjax_generateFunctionNode(funObj, data.primaryKey, {ico: true, label: true})
  165. ;
  166. funcNode.appendTo(cellNode);
  167. cellNode.appendTo(rowFunListNode);
  168. });
  169. }
  170. }
  171. })
  172. },
  173. render: function () {
  174. if(DBG)console.warn('P5UI__FeatureEditForm::render this.props', this.props)
  175. var formProps = Object.assign({}, this.props)
  176. delete formProps.children
  177. return h('form', Object.assign(formProps, {
  178. ref: function (node) { this.rootNode = node; }.bind(this),
  179. onSubmit: this.submit.bind(this)
  180. }), this.props.children)
  181. }
  182. })
  183. global.p5VendorJs['P5UI__FeatureEditForm'] = P5UI__FeatureEditForm
  184. var P5UI__FeatureCreateForm = createReactClass({
  185. componentDidMount: function () {
  186. if(DBG)console.warn('P5UI__FeatureCreateForm::componentDidMount this.rootNode', this.rootNode)
  187. jQuery(this.rootNode).find('textarea').autosize();
  188. jQuery(this.rootNode).find('.frm-help').popover({trigger:'hover'});
  189. jQuery(this.rootNode).find('.show-last-value input').on('input', function(e) {
  190. var input, btn, btnIco;
  191. input = jQuery(e.target);
  192. btn = input.next('.button-appendBack');
  193. btnIco = btn.find('.glyphicon');
  194. if (btn.attr('title') != input.val()) {
  195. btnIco.show();
  196. } else {
  197. btnIco.hide();
  198. }
  199. });
  200. jQuery(this.rootNode).find('.show-last-value').find('.button-appendBack').on('click', function(e) {
  201. var input, btn, btnIco;
  202. btn = jQuery(this);
  203. btnIco = btn.find('.glyphicon');
  204. input = btn.prev();
  205. if (input.is('input')) {
  206. if (btn.attr('title') != input.val()) {
  207. input.val(btn.attr('title'));
  208. btnIco.hide();
  209. }
  210. }
  211. });
  212. },
  213. submit: function (e) {
  214. if(DBG)console.warn('P5UI__FeatureCreateForm::submit this.rootNode', this.rootNode)
  215. e.preventDefault()
  216. var formData = {};
  217. jQuery(this.rootNode).serializeArray().map(function(i) { formData[i.name] = i.value; });// TODO: edit Widget - send only updated fields
  218. if(DBG)console.log('P5UI__FeatureCreateForm::submit formData', formData);
  219. var taskContLast = jQuery('#' + this.props._htmlID + '_task'),
  220. taskCont = taskContLast.parent();
  221. taskContLast.fadeOut('slow');
  222. var taskCnt = jQuery('<div id="' + this.props._htmlID + '_task" class="AjaxTableTaskCnt AjaxTable-loading"></div>').appendTo(taskCont);
  223. jQuery('<div class="alert alert-danger"><div style="padding:0 0 0 20px; background:url(./icon/loading.gif) no-repeat left top;"> save ... </div></div>').appendTo(taskCnt);
  224. function notifyAjaxCallback(data) {
  225. var notify = {}, outMsg = '';
  226. notify.type = (data && data.type)? data.type : '';
  227. notify.msg = (data && data.msg)? data.msg : '';
  228. switch (notify.type) {
  229. case 'success':
  230. if (!notify.msg) notify.msg = 'OK';
  231. break;
  232. case 'info':
  233. if (!notify.msg) notify.msg = '';
  234. break;
  235. case 'error':
  236. if (!notify.msg) notify.msg = 'Wystąpiły błędy';
  237. break;
  238. case 'warning':
  239. notify.type = 'warn';
  240. if (!notify.msg) notify.msg = 'Wystąpiły błędy';
  241. break;
  242. default:
  243. notify.msg = 'Nieznany błąd';
  244. if (data && data.errorCode) notify.msg += ' ' + data.errorCode;
  245. notify.type = '';
  246. }
  247. jQuery.notify(notify.msg, notify.type);
  248. var alertType = ('error' == data.type) ? 'danger' : data.type;
  249. outMsg = '<div class="alert alert-' + alertType + '">' + notify.msg + '</div>';
  250. return outMsg;
  251. }
  252. var tableLabelHtml = this.props.tableLabelHtml;
  253. global.fetch(this.props.ajaxSaveLegacyUrl, {
  254. headers: {
  255. 'Content-Type': 'application/json'
  256. },
  257. method: 'POST',
  258. credentials: 'same-origin',
  259. body: JSON.stringify(formData),
  260. }).then(function (response) {
  261. return response.json()
  262. }).then(function (data) {
  263. taskCnt.removeClass('AjaxTable-loading');
  264. taskCnt.empty();
  265. if(DBG)console.log('P5UI__FeatureCreateForm:: data: ', data)
  266. if ('success' === data.type) {
  267. var msg = 'OK';
  268. if (data.id && data.id > 0) msg = 'Utworzono pomyślnie rekord: ID = ' + data.id;
  269. else if (data.msg) msg = data.msg;
  270. var out = '<div class="container">';
  271. out += '<div class="alert alert-success">' + msg + '</div>';
  272. out += '<div class="breadcrumb">' +
  273. ' <a href="#" onclick="return tableAjaxBackToTable();" class="btn btn-link btn-sm"> <i class="glyphicon glyphicon-arrow-left"></i> Wróć do tabeli ' + tableLabelHtml + '</a>' +
  274. ' <a href="#EDIT/' + data.id + '" class="btn btn-link btn-sm"> <i class="glyphicon glyphicon-pencil"></i> Edytuj rekord ' + data.id + '</a>' +
  275. ' <a href="#CREATE/' + Math.random(1).toString().substr(2) + '" class="btn btn-link btn-sm"> <i class="glyphicon glyphicon-plus"></i> Dodaj nowy rekord</a></li>' +
  276. '</div>';
  277. out += '</div>';
  278. jQuery(out).appendTo(taskCnt);
  279. } else {
  280. var msg = (data.msg) ? data.msg : "Nieznany błąd!";
  281. var out = '<div class="container">' +
  282. '<div class="alert alert-danger">' +
  283. '<h4>Wystąpiły błędy!</h4>' + msg +
  284. '</div>';
  285. out += '<div class="breadcrumb">' +
  286. ' <a class="create-fix" href="#CREATE/' + Math.random(1).toString().substr(2) + '" class="btn btn-link btn-sm"> <i class="glyphicon glyphicon-arrow-left"></i> Wróć do formularza i popraw dane</a></li>' +
  287. '</div>' +
  288. '</div>';
  289. jQuery(out).appendTo(taskCnt);
  290. var taskContLastNode = taskContLast
  291. , taskCntNode = taskCnt;
  292. taskCnt.find('.create-fix').click(function(){
  293. taskCntNode.remove();
  294. taskContLastNode.fadeIn('slow');
  295. return false;
  296. });
  297. }
  298. }).catch(function (e) {
  299. taskCnt.removeClass('AjaxTable-loading');
  300. taskCnt.empty();
  301. var out = '<div class="container">' +
  302. '<div class="alert alert-danger">' +
  303. '<h4>Wystąpiły błędy!</h4>' + e +
  304. '</div>';
  305. out += '<div class="breadcrumb">' +
  306. ' <a class="create-fix" href="#CREATE/' + Math.random(1).toString().substr(2) + '" class="btn btn-link btn-sm"> <i class="glyphicon glyphicon-arrow-left"></i> Wróć do formularza i popraw dane</a></li>' +
  307. '</div>' +
  308. '</div>';
  309. jQuery(out).appendTo(taskCnt);
  310. var taskContLastNode = taskContLast
  311. , taskCntNode = taskCnt;
  312. taskCnt.find('.create-fix').click(function(){
  313. taskCntNode.remove();
  314. taskContLastNode.fadeIn('slow');
  315. return false;
  316. });
  317. if(DBG)console.log('P5UI__FeatureCreateForm:: e: ', e)
  318. });
  319. },
  320. render: function () {
  321. if(DBG)console.warn('P5UI__FeatureCreateForm::render this.props', this.props)
  322. var formProps = Object.assign({}, this.props)
  323. delete formProps.children
  324. return h('form', Object.assign(formProps, {
  325. ref: function (node) { this.rootNode = node; }.bind(this),
  326. onSubmit: this.submit.bind(this)
  327. }), this.props.children)
  328. }
  329. })
  330. global.p5VendorJs['P5UI__FeatureCreateForm'] = P5UI__FeatureCreateForm
  331. var P5UI__Typespecial = createReactClass({
  332. componentDidMount: function () {
  333. if(DBG)console.log('TS::componentDidMount (['+this.props.idField+'] '+this.props.fieldNamespace+') ...')
  334. // console.log('TS::componentDidMount (['+this.props.idField+'] '+this.props.fieldNamespace+') this.rootNode', this.rootNode) // OK
  335. var fldNode = jQuery(document.getElementById(this.props.fieldName)) // jQuery('#{$fName}')
  336. var tsNode = jQuery(this.rootNode) // jQuery('#ts-{$fName}')
  337. var ajaxDataUrlBase = this.props.ajaxDataUrlBase
  338. if (!fldNode && !tsNode) {
  339. console.log("BUG Missing fldNode or tsNode in P5UI__Typespecial")
  340. return;
  341. }
  342. if (!ajaxDataUrlBase) {
  343. console.log("BUG Missing ajaxDataUrlBase in P5UI__Typespecial")
  344. return;
  345. }
  346. fldNode.attr('name', '');
  347. fldNode.hide();
  348. if (fldNode.parent().hasClass('show-last-value')) {
  349. fldNode.parent().hide();
  350. }
  351. tsNode.attr('name', this.props.frmFldName)
  352. tsNode.attr('tabindex', fldNode.attr('tabindex'))
  353. var getFetchCallback = ('TypespecialVariable' === this.props.type)
  354. ? function (query, callback) {
  355. return function(res) {
  356. for (var i in res) {
  357. if (!res[i].name || res[i].id != res[i].name) {
  358. res[i].name = res[i].id + ': ' + res[i].name;
  359. }
  360. }
  361. callback(res);
  362. }
  363. }
  364. : function (query, callback) {
  365. return function(res) {
  366. var i, prefix, prefixLen;
  367. for (i in res) {
  368. prefix = '' + res[i].id;
  369. prefixLen = prefix.length;
  370. if (prefixLen > 0 && prefix !== res[i].name.substr(0, prefixLen)) {
  371. res[i].name = res[i].id + ': ' + res[i].name;
  372. }
  373. res[i]['\$order'] = 1 + (parseInt(i) || 0); // set order from request
  374. }
  375. callback(res);
  376. }
  377. }
  378. ;
  379. var scoreCallback = ('TypespecialVariable' === this.props.type)
  380. ? function(search) {
  381. var score = this.getScoreFunction(search);
  382. return function(item) {
  383. // console.log('score:item:', item, ', score:', score(item));
  384. if (search && search == item.id) {
  385. return 1;
  386. } else {
  387. return score(item);// score(item) * (1 + Math.min(item.watchers / 100, 1));
  388. }
  389. };
  390. }
  391. : function(query) {
  392. // console.log('Typespecial({$fName})::score: q(', query, ')');
  393. var score = this.getScoreFunction(query);
  394. return function(item) {
  395. var retScore = 0, lName = ('' + item.name).toLowerCase(), lQuery = query.toLowerCase();
  396. if (query.search(' ') > -1) {
  397. retScore = score(item);
  398. }
  399. else {
  400. if (lQuery + ':' == lName.substr(0, lQuery.length + 1)) {
  401. retScore = 100;
  402. } else if (lName.search(lQuery) > -1) {
  403. retScore = 90 - lName.search(lQuery);
  404. } else {
  405. retScore = 0;
  406. }
  407. }
  408. // console.log('Typespecial({$fName})::score: q(', lQuery, ') , retScore(', retScore, '), score(', score(item), '), item.name(', item.name, ') item.id:', item.id);
  409. return retScore;
  410. };
  411. }
  412. ;
  413. tsNode.selectize({
  414. theme: 'typespecial',
  415. valueField: 'id',
  416. labelField: 'id',
  417. searchField: 'name',
  418. sortField: ('TypespecialVariable' === this.props.type) ? 'name' : '$order', // TODO: skip in Typespecial - default sort field is '$order'
  419. create: this.props.create,
  420. delimiter: ';',
  421. dataAttr: 'typespecial',
  422. preload: this.props.preload || false,
  423. options: this.props.options,
  424. render: {
  425. item: function(item, escape) {
  426. return '<div>' +
  427. '<span class=\"name\">' + escape(item.name || item.id) + '</span>' +
  428. '</div>';
  429. },
  430. option: function(item, escape) {
  431. return '<div>' +
  432. '<span class=\"title\">' +
  433. escape(item.name || item.id) +
  434. '</span>' +
  435. '</div>';
  436. }
  437. },
  438. onItemAdd: function(value, item) {// Invoked when an item is selected.
  439. var curSel = this.options[value] || null, fieldExport;
  440. if (curSel && curSel.exports) {
  441. for (var i in curSel.exports) {
  442. fieldExport = jQuery('#ts-f' + i);
  443. if (fieldExport.length) {
  444. //console.log('--- onItemAdd fieldExport.selectize(',fieldExport.selectize,')', fieldExport);
  445. if (fieldExport.get(0).selectize) {
  446. fieldExport.get(0).selectize.addOption({id:curSel.exports[i], name:curSel.exports[i]});
  447. fieldExport.get(0).selectize.setValue(curSel.exports[i]);
  448. } else {
  449. //jQuery('#f' + i).val(curSel.exports[i]);
  450. }
  451. } else {
  452. fieldExport = jQuery('#f' + i);
  453. if (fieldExport.is('input')) {
  454. jQuery('#f' + i).val(curSel.exports[i]);
  455. } else if (fieldExport.is('select')) {
  456. //TODO: add option and select //jQuery('#f' + i).val(curSel.exports[i]);
  457. }
  458. }
  459. }
  460. }
  461. },
  462. score: scoreCallback,
  463. load: function(query, callback) {
  464. // if (!query.length) return callback(); // empty query at preload action
  465. $.ajax({
  466. url: ajaxDataUrlBase,
  467. data: 'q=' + encodeURIComponent(query),
  468. type: 'POST',
  469. error: function() {
  470. callback();
  471. },
  472. success: getFetchCallback(query, callback),
  473. });
  474. }
  475. });
  476. },
  477. render: function () {
  478. if(DBG)console.log('TS::render (['+this.props.idField+'] '+this.props.fieldNamespace+') props', this.props)
  479. return h('div', {
  480. 'className': "typespecial",
  481. 'style': { 'max-width': '366px' }
  482. }, [
  483. h('select', {
  484. 'ref': function (node) { this.rootNode = node; }.bind(this),
  485. }, this.props.children)
  486. ])
  487. }
  488. })
  489. global.p5VendorJs['P5UI__Typespecial'] = P5UI__Typespecial
  490. var P5UI__FeatureRowFunctions = createReactClass({
  491. componentDidMount: function () {
  492. if(DBG)console.warn('P5UI__FeatureRowFunctions::componentDidMount this.rootNode', this.rootNode)
  493. if (this.props.rawHtml) {
  494. if(DBG)console.warn('P5UI__FeatureRowFunctions::componentDidMount this.props.rawHtml', this.props.rawHtml)
  495. this.rootNode.innerHTML = this.props.rawHtml
  496. }
  497. },
  498. render: function () {
  499. var showLabels = this.props.showLabels || false
  500. var functions = this.props.functions || {}
  501. if (!this.props.id) return h('div', { 'className': 'alert alert-danger' }, "Missing ID")
  502. if(DBG)console.warn('P5UI__FeatureRowFunctions::render this.props', this.props)
  503. return h('div', {
  504. ref: function (node) { this.rootNode = node; }.bind(this),
  505. }, [
  506. ('hist' in functions)
  507. ? h('a', { className: "btn btn-xs btn-link", href: functions.hist.href.replace("{0}", this.props.id), title: functions.hist.title }, [ h('span', { className: functions.hist.ico }), h('span', { style: {'padding': '0 6px'} }, functions.hist.title) ])
  508. : null
  509. ,
  510. ('files' in functions)
  511. ? h('a', { className: "btn btn-xs btn-link", href: functions.files.href.replace("{0}", this.props.id), title: functions.files.title }, [ h('span', { className: functions.files.ico }), h('span', { style: {'padding': '0 6px'} }, functions.files.title) ])
  512. : null
  513. ,
  514. ('msgs' in functions)
  515. ? h('a', { className: "btn btn-xs btn-link", href: functions.msgs.href.replace("{0}", this.props.id), title: functions.msgs.title }, [ h('span', { className: functions.msgs.ico }), h('span', { style: {'padding': '0 6px'} }, functions.msgs.title) ])
  516. : null
  517. ,
  518. (this.props.viewMoreDropdown)
  519. ? h(P5UI__MoreFunctionsDropdownAjax, this.props.viewMoreDropdown)
  520. : null
  521. ,
  522. ])
  523. }
  524. })
  525. global.p5VendorJs['P5UI__FeatureRowFunctions'] = P5UI__FeatureRowFunctions
  526. var P5UI__MoreFunctionsDropdownAjax = createReactClass({
  527. componentDidMount: function () {
  528. jQuery(this.rootNode).popover({
  529. container: 'body',
  530. placement: 'bottom',
  531. trigger: 'click',
  532. // title: e.data.col + '<a href="#" class="glyphicon glyphicon-remove pull-right" onclick="return hidePopover();"></a>',
  533. title: 'Więcej funkcji dla rekordu nr ' + this.props.primaryKey, // '<div style="display:block;position:relative;padding:0 20px 0 0;">' + (this.props.friendly || colName) + ' <button type="button" class="close" onclick="return hidePopover();" style="position:absolute;right:0;top:0;">&times;</button>' + '</div>',
  534. html: true,
  535. content: 'TODO: list...', // this.renderListToString.bind(this),
  536. template: '' +
  537. '<div class="popover" role="tooltip" style="max-width:600px;width:400px;">' +
  538. '<div class="arrow"></div>' +
  539. '<div style="display:block;position:relative;">' +
  540. '<div class="popover-title">' +
  541. '</div>' +
  542. '</div>' +
  543. '<div class="popover-content"></div>' +
  544. '</div>'
  545. })
  546. this.setState({ 'isOpen': false })
  547. },
  548. componentWillUnmount: function () {
  549. if(DBG)console.warn("P5UI__MoreFunctionsDropdownAjax::componentWillUnmount")
  550. jQuery(this.rootNode).popover('destroy')
  551. },
  552. componentWillMount: function() {
  553. this._closeDropdownIfClickedOutside = function (event) {
  554. if (!this.state.isOpen) return;
  555. var idHtmlPopover = jQuery(this.rootNode).attr('aria-describedby')
  556. if(DBG)console.warn("P5UI__MoreFunctionsDropdownAjax::_closeDropdownIfClickedOutside idHtmlPopover:", idHtmlPopover)
  557. if (!idHtmlPopover) return;
  558. var popoverNode = document.getElementById(idHtmlPopover)
  559. var isClickedOutside = p5UI__clickedOutsideElement(popoverNode, event)
  560. if(DBG)console.warn("P5UI__MoreFunctionsDropdownAjax::_closeDropdownIfClickedOutside isClickedOutside:", isClickedOutside, 'this.rootNode', this.rootNode)
  561. if (isClickedOutside) {
  562. if (jQuery(this.rootNode).data("bs.popover")) {
  563. jQuery(this.rootNode).popover('hide')
  564. } else {
  565. popoverNode.parentNode.removeChild(popoverNode) // DOM removed before 'hide'
  566. }
  567. this.setState({
  568. isOpen: false
  569. }, this._unbindCloseDropdownIfClickedOutside.bind(this));
  570. }
  571. }.bind(this);
  572. this._bindCloseDropdownIfClickedOutside = function () {
  573. if(DBG)console.warn("P5UI__MoreFunctionsDropdownAjax::_bindCloseDropdownIfClickedOutside", this._closeDropdownIfClickedOutside)
  574. if (!document.addEventListener && document.attachEvent) {
  575. document.attachEvent('onclick', this._closeDropdownIfClickedOutside);
  576. } else {
  577. document.addEventListener('click', this._closeDropdownIfClickedOutside);
  578. }
  579. }.bind(this);
  580. this._unbindCloseDropdownIfClickedOutside = function () {
  581. if(DBG)console.warn("P5UI__MoreFunctionsDropdownAjax::_unbindCloseDropdownIfClickedOutside", this._closeDropdownIfClickedOutside)
  582. if (!document.removeEventListener && document.detachEvent) {
  583. document.detachEvent('onclick', this._closeDropdownIfClickedOutside);
  584. } else {
  585. document.removeEventListener('click', this._closeDropdownIfClickedOutside);
  586. }
  587. }.bind(this);
  588. },
  589. shouldComponentUpdate: function (nextProps, nextState) {
  590. if (!nextState) return false
  591. if (!this.state) return false
  592. if (this.state.rowFunctions !== nextState.rowFunctions) {
  593. var idHtmlPopover = jQuery(this.rootNode).attr('aria-describedby')
  594. if(DBG)console.warn("P5UI__MoreFunctionsDropdownAjax::shouldComponentUpdate idHtmlPopover:", idHtmlPopover, 'nextState.rowFunctions', nextState.rowFunctions)
  595. if (idHtmlPopover && nextState.rowFunctions && nextState.rowFunctions.length) {
  596. jQuery(document.getElementById(idHtmlPopover)).find('.popover-content').html(this.renderListToString(nextState.rowFunctions))
  597. } else {
  598. jQuery(document.getElementById(idHtmlPopover)).find('.popover-content').html('<p class="text-muted">Brak dodatkowych funkcji</p>')
  599. }
  600. }
  601. return false
  602. },
  603. renderListToString: function (list) {
  604. if(DBG)console.warn('P5UI__MoreFunctionsDropdownAjax::renderListToString - this.state:', this.state)
  605. var primaryKey = this.props.id
  606. var out = '<ul class="list-unstyled">' + list.map(function (fun) {
  607. return '<li><a href="'+fun.href.replace('{0}', primaryKey)+'" style="margin:0 2px;" title="'+fun.title+'">' +
  608. (fun.ico ? '<span class="'+fun.ico+'"></span> ' : '') +
  609. (fun.label || fun.title) +
  610. '</a></li>'
  611. }).join('') + '</ul>'
  612. if(DBG)console.warn('P5UI__MoreFunctionsDropdownAjax::renderListToString - out:', out)
  613. return out
  614. },
  615. onAjaxFetch: function (data) {
  616. this.setState({ rowFunctions: data.rowFunctions })
  617. },
  618. handleClick: function (e) {
  619. e.preventDefault()
  620. if (!this.props.uri) throw "Missing uri in P5UI__MoreFunctionsDropdownAjax"
  621. if (this.state.isOpen) {
  622. jQuery(this.rootNode).popover('hide')
  623. } else {
  624. jQuery(this.rootNode).popover('show')
  625. var idHtmlPopover = jQuery(this.rootNode).attr('aria-describedby')
  626. if (idHtmlPopover) {
  627. jQuery(document.getElementById(idHtmlPopover)).find('.popover-content').html('<p>Loading...</p>')
  628. }
  629. // global.fetch()
  630. // setTimeout(this.onAjaxFetch.bind(this, { msg: "Funkcje", type: "success", rowFunctions: [
  631. // { id: "msgs", ico: "glyphicon glyphicon-envelope", href: "index.php?_route=TableMsgs&_task=tableRow&idTable=13051&idRow=83", label: 'Wiadomości <span class="badge">0</badge>', title: "Wiadomości (0)" },
  632. // { ico: "glyphicon glyphicon-file", href: "https://biuro.biall-net.pl/dev-pl/se-master/index.php?_route=UrlAction_Ant&typeName=default_db:TEST_PERMS&primaryKey=83", label: 'Druki', title: "Druki" },
  633. // ] }), 1000)
  634. _popoverCellAjaxXhr = jQuery.ajax({
  635. type: 'GET',
  636. url: this.props.uri,
  637. dataType: 'json',
  638. contentType: "application/json; charset=utf-8",
  639. })
  640. .done(function(data, textStatus, jqXHR){
  641. if (data && 'success' === data.type) {
  642. var rowFunctions = (data.rowFunctions && data.rowFunctions.length > 0)
  643. ? data.rowFunctions
  644. : [];
  645. this.setState({ rowFunctions: data.rowFunctions })
  646. }
  647. }.bind(this))
  648. if (this.state.isOpen) this.setState({ 'isOpen': false })
  649. else this.setState({ 'isOpen': true }, this._bindCloseDropdownIfClickedOutside)
  650. }
  651. },
  652. render: function () {
  653. if(DBG)console.warn('P5UI__MoreFunctionsDropdownAjax::render this.props', this.props)
  654. return h('button', {
  655. className: 'btn btn-xs btn-link',
  656. ref: function (node) { this.rootNode = node; }.bind(this),
  657. onClick: this.handleClick.bind(this)
  658. }, [
  659. h('span', {'className':"glyphicon glyphicon-menu-hamburger"}),
  660. " Więcej "
  661. ]);
  662. }
  663. })
  664. global.p5VendorJs['P5UI__MoreFunctionsDropdownAjax'] = P5UI__MoreFunctionsDropdownAjax
  665. function buildDom(dom, target) {
  666. ReactDOM.render(buildReactNodeRec(dom), target)
  667. }
  668. function buildReactNodeRec(dom) {
  669. if (null === dom) return null
  670. if ('string' === typeof dom) return dom
  671. var nodeReactType = dom[0]
  672. if ('p5:' === dom[0].substr(0, 'p5:'.length)) { // eg. 'p5:Alert'
  673. nodeReactType = 'P5UI__' + dom[0].substr('p5:'.length)
  674. }
  675. if ('P5UI__' === nodeReactType.substr(0, 'P5UI__'.length)) {
  676. // TODO: if (!global.p5VendorJs[nodeReactType]) throw Exception or try to load external resource and complete after finish loading
  677. if (nodeReactType in global.p5VendorJs) nodeReactType = global.p5VendorJs[nodeReactType]
  678. }
  679. return h(nodeReactType,
  680. convertAttrsToReact(dom[0], dom[1]),
  681. (dom[2] && 'function' === typeof dom[2].map)
  682. ? dom[2].map(buildReactNodeRec)
  683. : dom[2]
  684. )
  685. }
  686. function convertAttrsToReact(tagName, attrs) {
  687. if (!attrs) return null
  688. if(DBG)console.log('todo convertAttrsToReact typeof attrs ('+ typeof attrs +') toString('+ attrs.toString() +')');
  689. if (!attrs.toString()) return null
  690. if ('class' in attrs) {
  691. attrs['className'] = attrs['class']
  692. delete attrs['class']
  693. }
  694. if ('style' in attrs) {
  695. DBG && console.log('DBG:convertAttrsToReact style', { style: attrs['style'], isString: 'string' === typeof attrs['style'], attrs });
  696. if ('string' === typeof attrs['style']) {
  697. attrs['style'] = attrs['style'].split(';').reduce(function (ret, st) {
  698. if (-1 !== st.indexOf(':')) {
  699. var stParts = st.split(':');
  700. var key = stParts[0];
  701. var val = stParts[1];
  702. // TODO: browser specific key: '--...'
  703. if (-1 !== key.indexOf('-')) {
  704. var keyParts = key.split('-');
  705. key = keyParts[0] + keyParts[1].charAt(0).toUpperCase() + keyParts[1].slice(1);
  706. }
  707. ret[key] = val;
  708. }
  709. return ret;
  710. }, {});
  711. DBG && console.log('DBG:convertAttrsToReact style fixed', { style: attrs['style'] });
  712. }
  713. }
  714. if ('input' === tagName && 'value' in attrs) { // fix input to uncontrolled
  715. attrs['defaultValue'] = attrs['value']
  716. delete attrs['value']
  717. }
  718. if ('input' === tagName && 'maxlength' in attrs) { // fix input to uncontrolled
  719. attrs['maxLength'] = attrs['maxlength']
  720. delete attrs['maxlength']
  721. }
  722. return attrs
  723. }
  724. global.p5UI__buildDom = buildDom
  725. })(window, window.p5VendorJs);