comboSearch.ex 4.9 KB

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