jquery.ui.widget.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. /*! jQuery UI - v1.12.1+0b7246b6eeadfa9e2696e22f3230f6452f8129dc - 2020-02-20
  2. * http://jqueryui.com
  3. * Includes: widget.js
  4. * Copyright jQuery Foundation and other contributors; Licensed MIT */
  5. /* global define, require */
  6. /* eslint-disable no-param-reassign, new-cap, jsdoc/require-jsdoc */
  7. (function (factory) {
  8. 'use strict';
  9. if (typeof define === 'function' && define.amd) {
  10. // AMD. Register as an anonymous module.
  11. define(['jquery'], factory);
  12. } else if (typeof exports === 'object') {
  13. // Node/CommonJS
  14. factory(require('jquery'));
  15. } else {
  16. // Browser globals
  17. factory(window.jQuery);
  18. }
  19. })(function ($) {
  20. ('use strict');
  21. $.ui = $.ui || {};
  22. $.ui.version = '1.12.1';
  23. /*!
  24. * jQuery UI Widget 1.12.1
  25. * http://jqueryui.com
  26. *
  27. * Copyright jQuery Foundation and other contributors
  28. * Released under the MIT license.
  29. * http://jquery.org/license
  30. */
  31. //>>label: Widget
  32. //>>group: Core
  33. //>>description: Provides a factory for creating stateful widgets with a common API.
  34. //>>docs: http://api.jqueryui.com/jQuery.widget/
  35. //>>demos: http://jqueryui.com/widget/
  36. // Support: jQuery 1.9.x or older
  37. // $.expr[ ":" ] is deprecated.
  38. if (!$.expr.pseudos) {
  39. $.expr.pseudos = $.expr[':'];
  40. }
  41. // Support: jQuery 1.11.x or older
  42. // $.unique has been renamed to $.uniqueSort
  43. if (!$.uniqueSort) {
  44. $.uniqueSort = $.unique;
  45. }
  46. var widgetUuid = 0;
  47. var widgetHasOwnProperty = Array.prototype.hasOwnProperty;
  48. var widgetSlice = Array.prototype.slice;
  49. $.cleanData = (function (orig) {
  50. return function (elems) {
  51. var events, elem, i;
  52. // eslint-disable-next-line eqeqeq
  53. for (i = 0; (elem = elems[i]) != null; i++) {
  54. // Only trigger remove when necessary to save time
  55. events = $._data(elem, 'events');
  56. if (events && events.remove) {
  57. $(elem).triggerHandler('remove');
  58. }
  59. }
  60. orig(elems);
  61. };
  62. })($.cleanData);
  63. $.widget = function (name, base, prototype) {
  64. var existingConstructor, constructor, basePrototype;
  65. // ProxiedPrototype allows the provided prototype to remain unmodified
  66. // so that it can be used as a mixin for multiple widgets (#8876)
  67. var proxiedPrototype = {};
  68. var namespace = name.split('.')[0];
  69. name = name.split('.')[1];
  70. var fullName = namespace + '-' + name;
  71. if (!prototype) {
  72. prototype = base;
  73. base = $.Widget;
  74. }
  75. if ($.isArray(prototype)) {
  76. prototype = $.extend.apply(null, [{}].concat(prototype));
  77. }
  78. // Create selector for plugin
  79. $.expr.pseudos[fullName.toLowerCase()] = function (elem) {
  80. return !!$.data(elem, fullName);
  81. };
  82. $[namespace] = $[namespace] || {};
  83. existingConstructor = $[namespace][name];
  84. constructor = $[namespace][name] = function (options, element) {
  85. // Allow instantiation without "new" keyword
  86. if (!this._createWidget) {
  87. return new constructor(options, element);
  88. }
  89. // Allow instantiation without initializing for simple inheritance
  90. // must use "new" keyword (the code above always passes args)
  91. if (arguments.length) {
  92. this._createWidget(options, element);
  93. }
  94. };
  95. // Extend with the existing constructor to carry over any static properties
  96. $.extend(constructor, existingConstructor, {
  97. version: prototype.version,
  98. // Copy the object used to create the prototype in case we need to
  99. // redefine the widget later
  100. _proto: $.extend({}, prototype),
  101. // Track widgets that inherit from this widget in case this widget is
  102. // redefined after a widget inherits from it
  103. _childConstructors: []
  104. });
  105. basePrototype = new base();
  106. // We need to make the options hash a property directly on the new instance
  107. // otherwise we'll modify the options hash on the prototype that we're
  108. // inheriting from
  109. basePrototype.options = $.widget.extend({}, basePrototype.options);
  110. $.each(prototype, function (prop, value) {
  111. if (!$.isFunction(value)) {
  112. proxiedPrototype[prop] = value;
  113. return;
  114. }
  115. proxiedPrototype[prop] = (function () {
  116. function _super() {
  117. return base.prototype[prop].apply(this, arguments);
  118. }
  119. function _superApply(args) {
  120. return base.prototype[prop].apply(this, args);
  121. }
  122. return function () {
  123. var __super = this._super;
  124. var __superApply = this._superApply;
  125. var returnValue;
  126. this._super = _super;
  127. this._superApply = _superApply;
  128. returnValue = value.apply(this, arguments);
  129. this._super = __super;
  130. this._superApply = __superApply;
  131. return returnValue;
  132. };
  133. })();
  134. });
  135. constructor.prototype = $.widget.extend(
  136. basePrototype,
  137. {
  138. // TODO: remove support for widgetEventPrefix
  139. // always use the name + a colon as the prefix, e.g., draggable:start
  140. // don't prefix for widgets that aren't DOM-based
  141. widgetEventPrefix: existingConstructor
  142. ? basePrototype.widgetEventPrefix || name
  143. : name
  144. },
  145. proxiedPrototype,
  146. {
  147. constructor: constructor,
  148. namespace: namespace,
  149. widgetName: name,
  150. widgetFullName: fullName
  151. }
  152. );
  153. // If this widget is being redefined then we need to find all widgets that
  154. // are inheriting from it and redefine all of them so that they inherit from
  155. // the new version of this widget. We're essentially trying to replace one
  156. // level in the prototype chain.
  157. if (existingConstructor) {
  158. $.each(existingConstructor._childConstructors, function (i, child) {
  159. var childPrototype = child.prototype;
  160. // Redefine the child widget using the same prototype that was
  161. // originally used, but inherit from the new version of the base
  162. $.widget(
  163. childPrototype.namespace + '.' + childPrototype.widgetName,
  164. constructor,
  165. child._proto
  166. );
  167. });
  168. // Remove the list of existing child constructors from the old constructor
  169. // so the old child constructors can be garbage collected
  170. delete existingConstructor._childConstructors;
  171. } else {
  172. base._childConstructors.push(constructor);
  173. }
  174. $.widget.bridge(name, constructor);
  175. return constructor;
  176. };
  177. $.widget.extend = function (target) {
  178. var input = widgetSlice.call(arguments, 1);
  179. var inputIndex = 0;
  180. var inputLength = input.length;
  181. var key;
  182. var value;
  183. for (; inputIndex < inputLength; inputIndex++) {
  184. for (key in input[inputIndex]) {
  185. value = input[inputIndex][key];
  186. if (
  187. widgetHasOwnProperty.call(input[inputIndex], key) &&
  188. value !== undefined
  189. ) {
  190. // Clone objects
  191. if ($.isPlainObject(value)) {
  192. target[key] = $.isPlainObject(target[key])
  193. ? $.widget.extend({}, target[key], value)
  194. : // Don't extend strings, arrays, etc. with objects
  195. $.widget.extend({}, value);
  196. // Copy everything else by reference
  197. } else {
  198. target[key] = value;
  199. }
  200. }
  201. }
  202. }
  203. return target;
  204. };
  205. $.widget.bridge = function (name, object) {
  206. var fullName = object.prototype.widgetFullName || name;
  207. $.fn[name] = function (options) {
  208. var isMethodCall = typeof options === 'string';
  209. var args = widgetSlice.call(arguments, 1);
  210. var returnValue = this;
  211. if (isMethodCall) {
  212. // If this is an empty collection, we need to have the instance method
  213. // return undefined instead of the jQuery instance
  214. if (!this.length && options === 'instance') {
  215. returnValue = undefined;
  216. } else {
  217. this.each(function () {
  218. var methodValue;
  219. var instance = $.data(this, fullName);
  220. if (options === 'instance') {
  221. returnValue = instance;
  222. return false;
  223. }
  224. if (!instance) {
  225. return $.error(
  226. 'cannot call methods on ' +
  227. name +
  228. ' prior to initialization; ' +
  229. "attempted to call method '" +
  230. options +
  231. "'"
  232. );
  233. }
  234. if (!$.isFunction(instance[options]) || options.charAt(0) === '_') {
  235. return $.error(
  236. "no such method '" +
  237. options +
  238. "' for " +
  239. name +
  240. ' widget instance'
  241. );
  242. }
  243. methodValue = instance[options].apply(instance, args);
  244. if (methodValue !== instance && methodValue !== undefined) {
  245. returnValue =
  246. methodValue && methodValue.jquery
  247. ? returnValue.pushStack(methodValue.get())
  248. : methodValue;
  249. return false;
  250. }
  251. });
  252. }
  253. } else {
  254. // Allow multiple hashes to be passed on init
  255. if (args.length) {
  256. options = $.widget.extend.apply(null, [options].concat(args));
  257. }
  258. this.each(function () {
  259. var instance = $.data(this, fullName);
  260. if (instance) {
  261. instance.option(options || {});
  262. if (instance._init) {
  263. instance._init();
  264. }
  265. } else {
  266. $.data(this, fullName, new object(options, this));
  267. }
  268. });
  269. }
  270. return returnValue;
  271. };
  272. };
  273. $.Widget = function (/* options, element */) {};
  274. $.Widget._childConstructors = [];
  275. $.Widget.prototype = {
  276. widgetName: 'widget',
  277. widgetEventPrefix: '',
  278. defaultElement: '<div>',
  279. options: {
  280. classes: {},
  281. disabled: false,
  282. // Callbacks
  283. create: null
  284. },
  285. _createWidget: function (options, element) {
  286. element = $(element || this.defaultElement || this)[0];
  287. this.element = $(element);
  288. this.uuid = widgetUuid++;
  289. this.eventNamespace = '.' + this.widgetName + this.uuid;
  290. this.bindings = $();
  291. this.hoverable = $();
  292. this.focusable = $();
  293. this.classesElementLookup = {};
  294. if (element !== this) {
  295. $.data(element, this.widgetFullName, this);
  296. this._on(true, this.element, {
  297. remove: function (event) {
  298. if (event.target === element) {
  299. this.destroy();
  300. }
  301. }
  302. });
  303. this.document = $(
  304. element.style
  305. ? // Element within the document
  306. element.ownerDocument
  307. : // Element is window or document
  308. element.document || element
  309. );
  310. this.window = $(
  311. this.document[0].defaultView || this.document[0].parentWindow
  312. );
  313. }
  314. this.options = $.widget.extend(
  315. {},
  316. this.options,
  317. this._getCreateOptions(),
  318. options
  319. );
  320. this._create();
  321. if (this.options.disabled) {
  322. this._setOptionDisabled(this.options.disabled);
  323. }
  324. this._trigger('create', null, this._getCreateEventData());
  325. this._init();
  326. },
  327. _getCreateOptions: function () {
  328. return {};
  329. },
  330. _getCreateEventData: $.noop,
  331. _create: $.noop,
  332. _init: $.noop,
  333. destroy: function () {
  334. var that = this;
  335. this._destroy();
  336. $.each(this.classesElementLookup, function (key, value) {
  337. that._removeClass(value, key);
  338. });
  339. // We can probably remove the unbind calls in 2.0
  340. // all event bindings should go through this._on()
  341. this.element.off(this.eventNamespace).removeData(this.widgetFullName);
  342. this.widget().off(this.eventNamespace).removeAttr('aria-disabled');
  343. // Clean up events and states
  344. this.bindings.off(this.eventNamespace);
  345. },
  346. _destroy: $.noop,
  347. widget: function () {
  348. return this.element;
  349. },
  350. option: function (key, value) {
  351. var options = key;
  352. var parts;
  353. var curOption;
  354. var i;
  355. if (arguments.length === 0) {
  356. // Don't return a reference to the internal hash
  357. return $.widget.extend({}, this.options);
  358. }
  359. if (typeof key === 'string') {
  360. // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
  361. options = {};
  362. parts = key.split('.');
  363. key = parts.shift();
  364. if (parts.length) {
  365. curOption = options[key] = $.widget.extend({}, this.options[key]);
  366. for (i = 0; i < parts.length - 1; i++) {
  367. curOption[parts[i]] = curOption[parts[i]] || {};
  368. curOption = curOption[parts[i]];
  369. }
  370. key = parts.pop();
  371. if (arguments.length === 1) {
  372. return curOption[key] === undefined ? null : curOption[key];
  373. }
  374. curOption[key] = value;
  375. } else {
  376. if (arguments.length === 1) {
  377. return this.options[key] === undefined ? null : this.options[key];
  378. }
  379. options[key] = value;
  380. }
  381. }
  382. this._setOptions(options);
  383. return this;
  384. },
  385. _setOptions: function (options) {
  386. var key;
  387. for (key in options) {
  388. this._setOption(key, options[key]);
  389. }
  390. return this;
  391. },
  392. _setOption: function (key, value) {
  393. if (key === 'classes') {
  394. this._setOptionClasses(value);
  395. }
  396. this.options[key] = value;
  397. if (key === 'disabled') {
  398. this._setOptionDisabled(value);
  399. }
  400. return this;
  401. },
  402. _setOptionClasses: function (value) {
  403. var classKey, elements, currentElements;
  404. for (classKey in value) {
  405. currentElements = this.classesElementLookup[classKey];
  406. if (
  407. value[classKey] === this.options.classes[classKey] ||
  408. !currentElements ||
  409. !currentElements.length
  410. ) {
  411. continue;
  412. }
  413. // We are doing this to create a new jQuery object because the _removeClass() call
  414. // on the next line is going to destroy the reference to the current elements being
  415. // tracked. We need to save a copy of this collection so that we can add the new classes
  416. // below.
  417. elements = $(currentElements.get());
  418. this._removeClass(currentElements, classKey);
  419. // We don't use _addClass() here, because that uses this.options.classes
  420. // for generating the string of classes. We want to use the value passed in from
  421. // _setOption(), this is the new value of the classes option which was passed to
  422. // _setOption(). We pass this value directly to _classes().
  423. elements.addClass(
  424. this._classes({
  425. element: elements,
  426. keys: classKey,
  427. classes: value,
  428. add: true
  429. })
  430. );
  431. }
  432. },
  433. _setOptionDisabled: function (value) {
  434. this._toggleClass(
  435. this.widget(),
  436. this.widgetFullName + '-disabled',
  437. null,
  438. !!value
  439. );
  440. // If the widget is becoming disabled, then nothing is interactive
  441. if (value) {
  442. this._removeClass(this.hoverable, null, 'ui-state-hover');
  443. this._removeClass(this.focusable, null, 'ui-state-focus');
  444. }
  445. },
  446. enable: function () {
  447. return this._setOptions({ disabled: false });
  448. },
  449. disable: function () {
  450. return this._setOptions({ disabled: true });
  451. },
  452. _classes: function (options) {
  453. var full = [];
  454. var that = this;
  455. options = $.extend(
  456. {
  457. element: this.element,
  458. classes: this.options.classes || {}
  459. },
  460. options
  461. );
  462. function bindRemoveEvent() {
  463. options.element.each(function (_, element) {
  464. var isTracked = $.map(that.classesElementLookup, function (elements) {
  465. return elements;
  466. }).some(function (elements) {
  467. return elements.is(element);
  468. });
  469. if (!isTracked) {
  470. that._on($(element), {
  471. remove: '_untrackClassesElement'
  472. });
  473. }
  474. });
  475. }
  476. function processClassString(classes, checkOption) {
  477. var current, i;
  478. for (i = 0; i < classes.length; i++) {
  479. current = that.classesElementLookup[classes[i]] || $();
  480. if (options.add) {
  481. bindRemoveEvent();
  482. current = $(
  483. $.uniqueSort(current.get().concat(options.element.get()))
  484. );
  485. } else {
  486. current = $(current.not(options.element).get());
  487. }
  488. that.classesElementLookup[classes[i]] = current;
  489. full.push(classes[i]);
  490. if (checkOption && options.classes[classes[i]]) {
  491. full.push(options.classes[classes[i]]);
  492. }
  493. }
  494. }
  495. if (options.keys) {
  496. processClassString(options.keys.match(/\S+/g) || [], true);
  497. }
  498. if (options.extra) {
  499. processClassString(options.extra.match(/\S+/g) || []);
  500. }
  501. return full.join(' ');
  502. },
  503. _untrackClassesElement: function (event) {
  504. var that = this;
  505. $.each(that.classesElementLookup, function (key, value) {
  506. if ($.inArray(event.target, value) !== -1) {
  507. that.classesElementLookup[key] = $(value.not(event.target).get());
  508. }
  509. });
  510. this._off($(event.target));
  511. },
  512. _removeClass: function (element, keys, extra) {
  513. return this._toggleClass(element, keys, extra, false);
  514. },
  515. _addClass: function (element, keys, extra) {
  516. return this._toggleClass(element, keys, extra, true);
  517. },
  518. _toggleClass: function (element, keys, extra, add) {
  519. add = typeof add === 'boolean' ? add : extra;
  520. var shift = typeof element === 'string' || element === null,
  521. options = {
  522. extra: shift ? keys : extra,
  523. keys: shift ? element : keys,
  524. element: shift ? this.element : element,
  525. add: add
  526. };
  527. options.element.toggleClass(this._classes(options), add);
  528. return this;
  529. },
  530. _on: function (suppressDisabledCheck, element, handlers) {
  531. var delegateElement;
  532. var instance = this;
  533. // No suppressDisabledCheck flag, shuffle arguments
  534. if (typeof suppressDisabledCheck !== 'boolean') {
  535. handlers = element;
  536. element = suppressDisabledCheck;
  537. suppressDisabledCheck = false;
  538. }
  539. // No element argument, shuffle and use this.element
  540. if (!handlers) {
  541. handlers = element;
  542. element = this.element;
  543. delegateElement = this.widget();
  544. } else {
  545. element = delegateElement = $(element);
  546. this.bindings = this.bindings.add(element);
  547. }
  548. $.each(handlers, function (event, handler) {
  549. function handlerProxy() {
  550. // Allow widgets to customize the disabled handling
  551. // - disabled as an array instead of boolean
  552. // - disabled class as method for disabling individual parts
  553. if (
  554. !suppressDisabledCheck &&
  555. (instance.options.disabled === true ||
  556. $(this).hasClass('ui-state-disabled'))
  557. ) {
  558. return;
  559. }
  560. return (typeof handler === 'string'
  561. ? instance[handler]
  562. : handler
  563. ).apply(instance, arguments);
  564. }
  565. // Copy the guid so direct unbinding works
  566. if (typeof handler !== 'string') {
  567. handlerProxy.guid = handler.guid =
  568. handler.guid || handlerProxy.guid || $.guid++;
  569. }
  570. var match = event.match(/^([\w:-]*)\s*(.*)$/);
  571. var eventName = match[1] + instance.eventNamespace;
  572. var selector = match[2];
  573. if (selector) {
  574. delegateElement.on(eventName, selector, handlerProxy);
  575. } else {
  576. element.on(eventName, handlerProxy);
  577. }
  578. });
  579. },
  580. _off: function (element, eventName) {
  581. eventName =
  582. (eventName || '').split(' ').join(this.eventNamespace + ' ') +
  583. this.eventNamespace;
  584. element.off(eventName);
  585. // Clear the stack to avoid memory leaks (#10056)
  586. this.bindings = $(this.bindings.not(element).get());
  587. this.focusable = $(this.focusable.not(element).get());
  588. this.hoverable = $(this.hoverable.not(element).get());
  589. },
  590. _delay: function (handler, delay) {
  591. var instance = this;
  592. function handlerProxy() {
  593. return (typeof handler === 'string'
  594. ? instance[handler]
  595. : handler
  596. ).apply(instance, arguments);
  597. }
  598. return setTimeout(handlerProxy, delay || 0);
  599. },
  600. _hoverable: function (element) {
  601. this.hoverable = this.hoverable.add(element);
  602. this._on(element, {
  603. mouseenter: function (event) {
  604. this._addClass($(event.currentTarget), null, 'ui-state-hover');
  605. },
  606. mouseleave: function (event) {
  607. this._removeClass($(event.currentTarget), null, 'ui-state-hover');
  608. }
  609. });
  610. },
  611. _focusable: function (element) {
  612. this.focusable = this.focusable.add(element);
  613. this._on(element, {
  614. focusin: function (event) {
  615. this._addClass($(event.currentTarget), null, 'ui-state-focus');
  616. },
  617. focusout: function (event) {
  618. this._removeClass($(event.currentTarget), null, 'ui-state-focus');
  619. }
  620. });
  621. },
  622. _trigger: function (type, event, data) {
  623. var prop, orig;
  624. var callback = this.options[type];
  625. data = data || {};
  626. event = $.Event(event);
  627. event.type = (type === this.widgetEventPrefix
  628. ? type
  629. : this.widgetEventPrefix + type
  630. ).toLowerCase();
  631. // The original event may come from any element
  632. // so we need to reset the target on the new event
  633. event.target = this.element[0];
  634. // Copy original event properties over to the new event
  635. orig = event.originalEvent;
  636. if (orig) {
  637. for (prop in orig) {
  638. if (!(prop in event)) {
  639. event[prop] = orig[prop];
  640. }
  641. }
  642. }
  643. this.element.trigger(event, data);
  644. return !(
  645. ($.isFunction(callback) &&
  646. callback.apply(this.element[0], [event].concat(data)) === false) ||
  647. event.isDefaultPrevented()
  648. );
  649. }
  650. };
  651. $.each({ show: 'fadeIn', hide: 'fadeOut' }, function (method, defaultEffect) {
  652. $.Widget.prototype['_' + method] = function (element, options, callback) {
  653. if (typeof options === 'string') {
  654. options = { effect: options };
  655. }
  656. var hasOptions;
  657. var effectName = !options
  658. ? method
  659. : options === true || typeof options === 'number'
  660. ? defaultEffect
  661. : options.effect || defaultEffect;
  662. options = options || {};
  663. if (typeof options === 'number') {
  664. options = { duration: options };
  665. }
  666. hasOptions = !$.isEmptyObject(options);
  667. options.complete = callback;
  668. if (options.delay) {
  669. element.delay(options.delay);
  670. }
  671. if (hasOptions && $.effects && $.effects.effect[effectName]) {
  672. element[method](options);
  673. } else if (effectName !== method && element[effectName]) {
  674. element[effectName](options.duration, options.easing, callback);
  675. } else {
  676. element.queue(function (next) {
  677. $(this)[method]();
  678. if (callback) {
  679. callback.call(element[0]);
  680. }
  681. next();
  682. });
  683. }
  684. };
  685. });
  686. });