comboSearch.ex 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. defmodule NITRO.Combo.Search do
  2. require NITRO
  3. require Record
  4. Record.defrecord(:state, lastMsg: [], timer: [], uid: [], pid: [], chunks: 0, value: [], reader: [], opts: [], feed: [])
  5. def start(uid, value, field, feed, opts) do
  6. state = state(uid: uid, pid: self(), value: value, reader: :erlang.apply(:kvs, :reader, [feed]), opts: opts, feed: feed)
  7. stop(uid, field)
  8. pi =
  9. NITRO.pi(
  10. module: NITRO.Combo.Search,
  11. table: :async,
  12. state: state,
  13. name: "comboSearch#{uid}",
  14. timeout: :brutal_kill,
  15. restart: :temporary
  16. )
  17. pid = :erlang.spawn_link(:nitro_pi, :start_link, [pi])
  18. :nitro_pi.cache(:async,{:async,"comboSearch#{uid}"},pid,:infinity)
  19. end
  20. def stop(uid, _field) do
  21. case :nitro_pi.pid(:async, "comboSearch#{uid}") do
  22. [] -> :ok
  23. pid ->
  24. :erlang.exit(pid, :kill)
  25. :nitro_pi.cache(:async, {:async, "comboSearch#{uid}"}, :undefined)
  26. end
  27. end
  28. def comboScroll(NITRO.comboScroll(uid: uid)) do
  29. case :nitro_pi.pid(:async, "comboSearch#{uid}") do
  30. [] -> []
  31. pid -> send(pid, {:filterComboValues, :append, []})
  32. end
  33. end
  34. def keyUp(NITRO.comboKey(uid: uid, value: value, dom: field, feed: feed, delegate: module)) do
  35. opts = [index: NITRO.Combo.index(module), field: field, delegate: module]
  36. comboContainer = :nitro.atom([:comboContainer, :nitro.to_list(field)])
  37. :nitro.display(:nitro.atom([:comboContainer, field]), :block)
  38. :nitro.clear(comboContainer)
  39. :nitro.wire("comboCloseFormById('#{:nitro.atom([:nitro.to_list(field), 'form'])}');")
  40. :nitro.wire("comboLookupChange('#{field}');")
  41. :nitro.wire(NITRO.bind(target: :nitro.to_binary(comboContainer), type: :scroll, postback: onscroll(uid, field, module)))
  42. start(uid, value, field, feed, opts)
  43. end
  44. def onscroll(uid, field, delegate), do:
  45. :erlang.iolist_to_binary([
  46. "if (event.target && (event.target.scrollTop + event.target.offsetHeight + 10 >= event.target.scrollHeight)) {",
  47. "ws.send(enc(tuple(atom('direct'),
  48. tuple(atom('comboScroll'),",
  49. "bin('#{uid}'),",
  50. "bin('#{field}'),",
  51. "atom('#{delegate}'))",
  52. ")));",
  53. "}"
  54. ])
  55. def proc(:init, NITRO.pi(state: state(value: v) = st) = pi) do
  56. send(self(), {:filterComboValues, :init, v})
  57. {:ok, NITRO.pi(pi, state: state(st, lastMsg: :erlang.timestamp(), timer: ping(100)))}
  58. end
  59. def proc({:check}, NITRO.pi(state: state(timer: t, lastMsg: {_, sec, _}) = st) = pi) do
  60. :erlang.cancel_timer(t)
  61. case :erlang.timestamp() do
  62. {_, x, _} when x - sec >= 60 -> {:stop, :normal, NITRO.pi(pi, state: state(st, timer: []))}
  63. _ -> {:noreply, NITRO.pi(pi, state: state(st, timer: ping(10000)))}
  64. end
  65. end
  66. def proc({:filterComboValues, cmd, value0}, NITRO.pi(state: state(chunks: chunks) = st) = pi) do
  67. state(uid: uid, feed: feed, reader: r, value: prev, pid: pid, opts: opts) = st
  68. m = Keyword.get(opts, :delegate, [])
  69. field = Keyword.get(opts, :field, [])
  70. value = case cmd do :append -> prev; _ -> :string.lowercase(:unicode.characters_to_list(value0, :unicode)) end
  71. r1 = :erlang.apply(:kvs, :take, [:erlang.apply(:kvs, :setfield, [r, :args, 10])])
  72. case :erlang.apply(:kvs, :field, [r1, :args]) do
  73. [] ->
  74. send(pid, {:direct, NITRO.comboInsert(uid: uid, dom: field, delegate: m, chunks: chunks, status: :finished)})
  75. {:stop, :normal, NITRO.pi(pi, state: state(st, reader: :erlang.apply(:kvs, :setfield, [r1, :args, []])))}
  76. rows ->
  77. filtered =
  78. case value do
  79. 'all' -> rows
  80. _ ->
  81. :lists.filter(fn x -> :lists.any(&filter(value, x, &1), Keyword.get(opts, :index, [])) end, rows)
  82. end
  83. newChunks = chunks + length(filtered)
  84. send(pid, {:direct, NITRO.comboInsert(uid: uid, dom: field, delegate: m, chunks: newChunks, feed: feed, rows: filtered)})
  85. chunks < 100 and
  86. case :nitro_pi.pid(:async, "comboSearch#{uid}") do [] -> []; pid -> send(pid, {:filterComboValues, cmd, value0}) end
  87. {:noreply, NITRO.pi(pi, state: state(st, lastMsg: :erlang.timestamp(), value: value, chunks: newChunks, reader: :erlang.apply(:kvs, :setfield, [r1, :args, []])))}
  88. end
  89. end
  90. def proc(_, pi), do: {:noreply, pi}
  91. def ping(milliseconds), do: :erlang.send_after(milliseconds, self(), {:check})
  92. defp filter(val, obj, i) do
  93. fld =
  94. if is_function(i) do
  95. i.(obj)
  96. else
  97. fldVal = :erlang.apply(:kvs, :field, [obj, i])
  98. if fldVal == elem(obj, 0), do: :nitro.to_list(fldVal), else: fldVal
  99. end |> NITRO.Combo.to_list()
  100. fld != elem(obj, 0) and :string.rstr(:string.lowercase(:nitro.to_list(fld)), val) > 0
  101. end
  102. end