targetj 1.0.141 → 1.0.142

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,15 +6,15 @@ Welcome to TargetJS, a powerful JavaScript UI framework and library that you mig
6
6
 
7
7
  Additionally, **TargetJS** executes targets in the exact order they appear in the code. Each target can process new data based on the outcome of the previous one, creating a structured flow. Each target can also conditionally opt out of execution or re-execute a number of times when specific conditions are met.
8
8
 
9
- Targets cannot be called directly; instead, they are **activated** and then executed sequentially in the order of their activation. This approach ensures **predictable execution** and a consistent flow.
9
+ Targets cannot also be called directly; instead, they are **activated** and then executed sequentially in the order of their activation. This approach ensures **predictable execution** and a consistent flow.
10
10
 
11
11
  ## TargetJ: A Unique Computational Paradigm
12
12
 
13
13
  TargetJ introduces a unique paradigm by blending multiple computational models:
14
14
 
15
15
  - **Turing Completeness**: targets can execute continuously, modify values dynamically, and conditionally skip execution.
16
- - **Von Neumann Execution Model**: Targets are executed in the order they appear in the code or based on their activation order.
17
- - **Functional Programming**: Targets can be composed to transform data in a pipeline-like manner, similar to functional programming constructs.
16
+ - **Von Neumann Execution Model**: Targets are executed in the order they appear in the code or based on their activation order. They cannot be called directly.
17
+ - **Functional Programming**: Targets can be composed to transform data in a pipeline-like manner, similar to functional programming constructs. Each target can access the result of the previous target using this.prevTargetValue and determine if it has changed with isPrevTargetUpdate(), making targets easier to test.
18
18
 
19
19
  We believe TargetJS' new programming paradigm will enhance productivity and make the coding experience more enjoyable and user-centric.
20
20
 
@@ -74,7 +74,7 @@ TargetJS enables developers to control the execution timing of targets, allowing
74
74
 
75
75
  ### Unified Approach for Development
76
76
 
77
- Targets offer a **unified solution** for UI, animation, event handling, API calls, and state management, rather than working with multiple technologies,
77
+ Targets offer a **unified solution** for **UI**, **animation**, **event handling**, **API calls**, and **state management**, rather than working with multiple technologies,
78
78
  commands, and approaches. This simplifies development and makes **TargetJS** easier to learn.
79
79
 
80
80
  ### High Performance for large Lists
@@ -123,7 +123,8 @@ Targets provide a **unified approach** for addressing **animation**, **user inte
123
123
  ### Draggable Animation Example
124
124
 
125
125
  In our first example, `color`, `html`, `textAlign`, `moves`, and `animate` are all targets. These targets are executed in the same order they appear in the program. `color`, `html`, `textAlign`, `moves` get competed and their life cycle end.
126
- The `moves` target prepares the data required for the `animate` target before execution. The `animate` target accesses the moves generated by the previous target using `this.prevTargetValue`, then generates a list of imperative targets for each style included in each move.
126
+ The `moves` target prepares the data required for the `animate` target before execution. The `animate` target accesses the moves generated by the previous target using `this.prevTargetValue`, then generates a list of imperative targets for each style included in each move. Although it is not needed in this example, we can also use `isPrevTargetUpdated()` to check if the previous target value has been updated, allowing us to skip unnecessary execution and improve performance.
127
+
127
128
  The target `animate` remains active with an indefinite lifecycle specified by the `loop` property. After each animation cycle, there is a one-second pause, defined by the `interval` property. Both `loop` and `interval` can also be defined as methods, which will be explained further below. The `setTarget` method defines an imperative target, which is also explained in more detail below, executes the assigment in 30 steps. The `animate` target starts a new cycle after all the imperative targets have been completed or at least one second pass specified in the interval value given that the imperative targets get executed less than 1 second.
128
129
 
129
130
  You'll also find `quickStart`, the first argument in the `TModel` constructor. If an HTML element with the same ID already exists on the page, it will be used in the new instance of `TModel`, and the animation will be applied to it. If no such element exists, TargetJS will create one.
@@ -146,7 +147,7 @@ App(new TModel('quickStart', {
146
147
  ],
147
148
  animate: {
148
149
  loop: true,
149
- cycles: 2,
150
+ cycles() { return this.prevTargetValue.length - 1; },
150
151
  interval: 1000,
151
152
  value(cycle) {
152
153
  const move = this.prevTargetValue[cycle];
@@ -170,9 +171,11 @@ This example demonstrates how to handle scroll events and implement a simple inf
170
171
 
171
172
  Internally, TargetJS maintains a tree-like structure to track the visible branches of the tree, optimizing the performance of the scroller. You can opt out of tree-structure optimization by setting `shouldBeBracketed` target to false.
172
173
 
174
+ Each item's width will start at 100px and expand to 250px over 15 steps, executed as quickly as the device allows. Simultaneously, the background will transition from yellow to purple in 15 steps, with each step occurring every 15 milliseconds.
175
+
173
176
  If you inspect the HTML elements in the browser's developer tools, you'll notice that the scroller's elements are not nested inside the container. This is because nesting is another target that can dynamically control how elements are nested. This facilitates the reuse of HTML elements and opens the door to new user experiences.
174
177
 
175
- ![Single page app](https://targetjs.io/img/infiniteScrolling5.gif)
178
+ ![Single page app](https://targetjs.io/img/infiniteScrolling6.gif)
176
179
 
177
180
  ```bash
178
181
  import { App, TModel, getEvents, getScreenHeight, getScreenWidth, } from "targetj";
@@ -182,9 +185,9 @@ App(new TModel({
182
185
  children() {
183
186
  const childrenCount = this.getChildren().length;
184
187
  return Array.from({ length: 10 }, (_, i) =>
185
- new TModel('scrollItem', {
186
- width: 300,
187
- background: '#B388FF',
188
+ new TModel('scrollItem', {
189
+ width: [{ list: [100, 250] }, 15],
190
+ background: [{ list: ["#FCE961", "#B388FF"] }, 15, 15],
188
191
  height: 32,
189
192
  color: '#C2FC61',
190
193
  textAlign: 'center',
@@ -77,9 +77,10 @@ var BaseModel = exports.BaseModel = /*#__PURE__*/function () {
77
77
  this.targetValues = {};
78
78
  this.activeTargetMap = {};
79
79
  this.activeTargetList = [];
80
+ var targetNames = Object.keys(this.targets);
80
81
  var domExists = _$Dom.$Dom.query("#".concat(this.oid));
81
82
  if (!domExists && !this.excludeDefaultStyling()) {
82
- Object.entries(_TModelUtil.TModelUtil.defaultTargets()).forEach(function (_ref) {
83
+ Object.entries(_TModelUtil.TModelUtil.defaultTargetStyles()).forEach(function (_ref) {
83
84
  var _ref2 = _slicedToArray(_ref, 2),
84
85
  key = _ref2[0],
85
86
  value = _ref2[1];
@@ -91,28 +92,32 @@ var BaseModel = exports.BaseModel = /*#__PURE__*/function () {
91
92
  this.targets['reuseDomDefinition'] = true;
92
93
  }
93
94
  Object.keys(this.targets).forEach(function (key) {
94
- _this.processNewTarget(key);
95
+ _this.processNewTarget(key, targetNames);
95
96
  });
96
97
  }
97
98
  }, {
98
99
  key: "processNewTarget",
99
- value: function processNewTarget(key) {
100
+ value: function processNewTarget(key, targetNames) {
101
+ if (!_TUtil.TUtil.isDefined(this.targets[key])) {
102
+ this.delVal('key');
103
+ return;
104
+ }
105
+ _TargetUtil.TargetUtil.bindTarget(this, key, targetNames);
106
+ var cleanKey = _TargetUtil.TargetUtil.getTargetName(key);
100
107
  var isInactiveKey = key.startsWith('_');
101
- if (isInactiveKey) {
102
- var newKey = key.slice(1);
103
- this.targets[newKey] = _typeof(this.targets[key]) === 'object' && this.targets[key].value ? this.targets[key] : {
104
- value: this.targets[key]
105
- };
106
- this.targets[newKey].active = false;
108
+ if (cleanKey !== key) {
109
+ if (isInactiveKey) {
110
+ this.targets[cleanKey] = _typeof(this.targets[key]) === 'object' && this.targets[key].value ? this.targets[key] : {
111
+ value: this.targets[key]
112
+ };
113
+ this.targets[cleanKey].active = false;
114
+ } else {
115
+ this.targets[cleanKey] = this.targets[key];
116
+ }
107
117
  delete this.targets[key];
108
- key = newKey;
118
+ key = cleanKey;
109
119
  }
110
120
  var target = this.targets[key];
111
- if (!_TUtil.TUtil.isDefined(target)) {
112
- this.delVal('key');
113
- return;
114
- }
115
- _TargetUtil.TargetUtil.bindTargetName(this, key);
116
121
  if (_TargetUtil.TargetUtil.allEventMap[key] || _TargetUtil.TargetUtil.internalEventMap[key]) {
117
122
  if (!this.eventTargetMap[key]) {
118
123
  this.eventTargetList.push(key);
@@ -7,6 +7,7 @@ exports.LoadingManager = void 0;
7
7
  var _$Dom = require("./$Dom.js");
8
8
  var _TUtil = require("./TUtil.js");
9
9
  var _App = require("./App.js");
10
+ var _TargetUtil = require("./TargetUtil.js");
10
11
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
11
12
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
12
13
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -23,6 +24,7 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
23
24
  function LoadingManager() {
24
25
  _classCallCheck(this, LoadingManager);
25
26
  this.cacheMap = {};
27
+ this.tmodelKeyMap = {};
26
28
  this.fetchingAPIMap = {};
27
29
  this.fetchingImageMap = {};
28
30
  }
@@ -37,11 +39,13 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
37
39
  }));
38
40
  }
39
41
  } else if (fetchMap[fetchId]) {
42
+ this.addToTModelKeyMap(tmodel, tmodel.key);
40
43
  fetchMap[fetchId].targets.push({
41
44
  tmodel: tmodel,
42
45
  targetName: tmodel.key
43
46
  });
44
47
  } else {
48
+ this.addToTModelKeyMap(tmodel, tmodel.key);
45
49
  fetchMap[fetchId] = {
46
50
  fetchId: fetchId,
47
51
  cacheId: cacheId,
@@ -57,6 +61,24 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
57
61
  }
58
62
  return fetchId;
59
63
  }
64
+ }, {
65
+ key: "addToTModelKeyMap",
66
+ value: function addToTModelKeyMap(tmodel, targetName) {
67
+ var key = "".concat(tmodel.oid, " ").concat(targetName);
68
+ this.tmodelKeyMap[key] = true;
69
+ }
70
+ }, {
71
+ key: "removeFromTModelKeyMap",
72
+ value: function removeFromTModelKeyMap(tmodel, targetName) {
73
+ var key = "".concat(tmodel.oid, " ").concat(targetName);
74
+ delete this.tmodelKeyMap[key];
75
+ }
76
+ }, {
77
+ key: "isInTModelKeyMap",
78
+ value: function isInTModelKeyMap(tmodel, targetName) {
79
+ var key = "".concat(tmodel.oid, " ").concat(targetName);
80
+ return this.tmodelKeyMap[key];
81
+ }
60
82
  }, {
61
83
  key: "fetch",
62
84
  value: function fetch(tmodel, url, query, cacheId) {
@@ -100,6 +122,7 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
100
122
  }, {
101
123
  key: "handleSuccess",
102
124
  value: function handleSuccess(fetchStatus, result) {
125
+ var _this3 = this;
103
126
  var fetchTime = _TUtil.TUtil.now();
104
127
  var fetchId = fetchStatus.fetchId,
105
128
  cacheId = fetchStatus.cacheId,
@@ -115,9 +138,12 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
115
138
  var _tmodel$targets$targe;
116
139
  var tmodel = _ref.tmodel,
117
140
  targetName = _ref.targetName;
141
+ _this3.removeFromTModelKeyMap(tmodel, targetName);
118
142
  if (typeof ((_tmodel$targets$targe = tmodel.targets[targetName]) === null || _tmodel$targets$targe === void 0 ? void 0 : _tmodel$targets$targe.onSuccess) === 'function' && tmodel.isTargetEnabled(targetName)) {
119
143
  tmodel.targets[targetName].onSuccess.call(tmodel, res);
120
144
  }
145
+ tmodel.val(targetName, result);
146
+ _TargetUtil.TargetUtil.shouldActivateNextTarget(tmodel, targetName);
121
147
  });
122
148
  delete fetchMap[fetchId];
123
149
  if (cacheId) {
@@ -128,6 +154,7 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
128
154
  }, {
129
155
  key: "handleError",
130
156
  value: function handleError(fetchStatus, error) {
157
+ var _this4 = this;
131
158
  var fetchTime = _TUtil.TUtil.now();
132
159
  var fetchId = fetchStatus.fetchId,
133
160
  cacheId = fetchStatus.cacheId,
@@ -143,9 +170,12 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
143
170
  var _tmodel$targets$targe2;
144
171
  var tmodel = _ref2.tmodel,
145
172
  targetName = _ref2.targetName;
173
+ _this4.removeFromTModelKeyMap(tmodel, targetName);
146
174
  if (typeof ((_tmodel$targets$targe2 = tmodel.targets[targetName]) === null || _tmodel$targets$targe2 === void 0 ? void 0 : _tmodel$targets$targe2.onError) === 'function') {
147
175
  tmodel.targets[targetName].onError.call(tmodel, res);
148
176
  }
177
+ tmodel.actualValues[targetName] = res;
178
+ _TargetUtil.TargetUtil.shouldActivateNextTarget(tmodel, targetName);
149
179
  });
150
180
  delete fetchMap[fetchId];
151
181
  if (cacheId) {
@@ -156,15 +186,15 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
156
186
  }, {
157
187
  key: "ajaxAPI",
158
188
  value: function ajaxAPI(url, query, fetchStatus) {
159
- var _this3 = this;
189
+ var _this5 = this;
160
190
  var defaultQuery = {
161
191
  dataType: "json",
162
192
  type: "GET",
163
193
  success: function success(dataList) {
164
- return _this3.handleSuccess(fetchStatus, dataList);
194
+ return _this5.handleSuccess(fetchStatus, dataList);
165
195
  },
166
196
  error: function error(textStatus) {
167
- return _this3.handleError(fetchStatus, textStatus);
197
+ return _this5.handleError(fetchStatus, textStatus);
168
198
  }
169
199
  };
170
200
  _$Dom.$Dom.ajax(_objectSpread(_objectSpread({}, defaultQuery), {}, {
@@ -176,7 +206,7 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
176
206
  }, {
177
207
  key: "loadImage",
178
208
  value: function loadImage(src, fetchStatus) {
179
- var _this4 = this;
209
+ var _this6 = this;
180
210
  var image = new Image();
181
211
  image.src = src;
182
212
  image.onload = function () {
@@ -185,10 +215,10 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
185
215
  height: image.height,
186
216
  src: image.src
187
217
  };
188
- _this4.handleSuccess(fetchStatus, result);
218
+ _this6.handleSuccess(fetchStatus, result);
189
219
  };
190
220
  image.onerror = image.onerror = function () {
191
- _this4.handleError(fetchStatus, "not found");
221
+ _this6.handleError(fetchStatus, "not found");
192
222
  };
193
223
  }
194
224
  }]);
@@ -53,8 +53,8 @@ var TModelUtil = exports.TModelUtil = /*#__PURE__*/function () {
53
53
  };
54
54
  }
55
55
  }, {
56
- key: "defaultTargets",
57
- value: function defaultTargets() {
56
+ key: "defaultTargetStyles",
57
+ value: function defaultTargetStyles() {
58
58
  return {
59
59
  position: 'absolute',
60
60
  left: 0,
@@ -29,6 +29,7 @@ var TargetExecutor = exports.TargetExecutor = /*#__PURE__*/function () {
29
29
  value: function executeDeclarativeTarget(tmodel, key) {
30
30
  TargetExecutor.resolveTargetValue(tmodel, key);
31
31
  TargetExecutor.updateTarget(tmodel, tmodel.targetValues[key], key);
32
+ _TargetUtil.TargetUtil.shouldActivateNextTarget(tmodel, key);
32
33
  }
33
34
  }, {
34
35
  key: "executeImperativeTarget",
@@ -191,6 +191,7 @@ var TargetManager = exports.TargetManager = /*#__PURE__*/function () {
191
191
  }
192
192
  tmodel.incrementTargetStep(key);
193
193
  tmodel.updateTargetStatus(key);
194
+ _TargetUtil.TargetUtil.shouldActivateNextTarget(tmodel, key);
194
195
  if (tmodel.getTargetStep(key) < steps) {
195
196
  (0, _App.getRunScheduler)().schedule(interval, "".concat(tmodel.oid, "---").concat(key, "-").concat(step, "/").concat(steps, "-").concat(cycle, "-").concat(interval));
196
197
  return;
@@ -72,18 +72,29 @@ var TargetUtil = exports.TargetUtil = /*#__PURE__*/function () {
72
72
  return autoHandleEvents;
73
73
  }
74
74
  }, {
75
- key: "bindTargetName",
76
- value: function bindTargetName(instance, key) {
77
- var target = instance.targets[key];
78
- var keys = Object.keys(instance.targets);
75
+ key: "getTargetName",
76
+ value: function getTargetName(key) {
77
+ if (!key) {
78
+ return key;
79
+ }
80
+ var cleanKey = key.startsWith('_') ? key.slice(1) : key;
81
+ cleanKey = cleanKey.endsWith('$') ? cleanKey.slice(0, -1) : cleanKey;
82
+ return cleanKey;
83
+ }
84
+ }, {
85
+ key: "bindTarget",
86
+ value: function bindTarget(tmodel, key) {
87
+ var keys = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Object.keys(tmodel.targets);
88
+ var target = tmodel.targets[key];
79
89
  var keyIndex = keys.indexOf(key);
80
- var prevKey = keyIndex > 0 ? keys[keyIndex - 1] : undefined;
90
+ var prevKey = keyIndex > 0 ? TargetUtil.getTargetName(keys[keyIndex - 1]) : undefined;
91
+ var nextKey = keyIndex < keys.length - 1 ? keys[keyIndex + 1] : undefined;
81
92
  var getPrevValue = function getPrevValue() {
82
- return prevKey !== undefined ? instance.val(prevKey) : undefined;
93
+ return prevKey !== undefined ? tmodel.val(prevKey) : undefined;
83
94
  };
84
- var lastPrevUpdateTime = prevKey !== undefined ? instance.getActualValueLastUpdate(prevKey) : undefined;
95
+ var lastPrevUpdateTime = prevKey !== undefined ? tmodel.getActualValueLastUpdate(prevKey) : undefined;
85
96
  var getPrevUpdateTime = function getPrevUpdateTime() {
86
- return prevKey !== undefined ? instance.getActualValueLastUpdate(prevKey) : undefined;
97
+ return prevKey !== undefined ? tmodel.getActualValueLastUpdate(prevKey) : undefined;
87
98
  };
88
99
  var isPrevTargetUpdated = function isPrevTargetUpdated() {
89
100
  var currentPrevUpdateTime = getPrevUpdateTime();
@@ -95,6 +106,18 @@ var TargetUtil = exports.TargetUtil = /*#__PURE__*/function () {
95
106
  }
96
107
  return currentPrevUpdateTime !== lastPrevUpdateTime;
97
108
  };
109
+ var doesNextTargetUsePrevValue = nextKey && nextKey.endsWith('$') ? true : false;
110
+ if (doesNextTargetUsePrevValue) {
111
+ if (_typeof(target) === 'object' && !Array.isArray(target)) {
112
+ target.activateNextTarget = TargetUtil.getTargetName(nextKey);
113
+ } else {
114
+ tmodel.targets[key] = {
115
+ value: target,
116
+ activateNextTarget: TargetUtil.getTargetName(nextKey)
117
+ };
118
+ target = tmodel.targets[key];
119
+ }
120
+ }
98
121
  if (_typeof(target) === 'object') {
99
122
  var stepPattern = /^on[A-Za-z]+Step$/;
100
123
  var endPattern = /^on[A-Za-z]+End$/;
@@ -104,7 +127,7 @@ var TargetUtil = exports.TargetUtil = /*#__PURE__*/function () {
104
127
  var originalMethod = target[method];
105
128
  target[method] = function () {
106
129
  var _getPrevUpdateTime;
107
- this.key = key;
130
+ this.key = TargetUtil.getTargetName(key);
108
131
  this.prevTargetValue = getPrevValue();
109
132
  this.isPrevTargetUpdated = isPrevTargetUpdated;
110
133
  var result = originalMethod.apply(this, arguments);
@@ -115,9 +138,9 @@ var TargetUtil = exports.TargetUtil = /*#__PURE__*/function () {
115
138
  });
116
139
  } else if (typeof target === 'function') {
117
140
  var originalFunction = target;
118
- instance.targets[key] = function () {
141
+ tmodel.targets[key] = function () {
119
142
  var _getPrevUpdateTime2;
120
- this.key = key;
143
+ this.key = TargetUtil.getTargetName(key);
121
144
  this.prevTargetValue = getPrevValue();
122
145
  this.isPrevTargetUpdated = isPrevTargetUpdated;
123
146
  var result = originalFunction.apply(this, arguments);
@@ -126,6 +149,21 @@ var TargetUtil = exports.TargetUtil = /*#__PURE__*/function () {
126
149
  };
127
150
  }
128
151
  }
152
+ }, {
153
+ key: "shouldActivateNextTarget",
154
+ value: function shouldActivateNextTarget(tmodel, key) {
155
+ var _tmodel$targets$key;
156
+ if ((_tmodel$targets$key = tmodel.targets[key]) !== null && _tmodel$targets$key !== void 0 && _tmodel$targets$key.activateNextTarget) {
157
+ var _tmodel$targets$key2;
158
+ var targetName = (_tmodel$targets$key2 = tmodel.targets[key]) === null || _tmodel$targets$key2 === void 0 ? void 0 : _tmodel$targets$key2.activateNextTarget;
159
+ if (!(0, _App.getLoader)().isInTModelKeyMap(tmodel, key)) {
160
+ if (tmodel.isTargetImperative(targetName)) {
161
+ tmodel.targetValues[targetName].isImperative = false;
162
+ }
163
+ tmodel.activate(targetName);
164
+ }
165
+ }
166
+ }
129
167
  }, {
130
168
  key: "isValueStepsCycleArray",
131
169
  value: function isValueStepsCycleArray(arr) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "targetj",
3
- "version": "1.0.141",
3
+ "version": "1.0.142",
4
4
  "keywords": [
5
5
  "targetjs"
6
6
  ],