diff options
22 files changed, 932 insertions, 457 deletions
diff --git a/js/components/contactDetails/contactDetails_controller.js b/js/components/contactDetails/contactDetails_controller.js index 46b4f6e3..cb7fa403 100644 --- a/js/components/contactDetails/contactDetails_controller.js +++ b/js/components/contactDetails/contactDetails_controller.js @@ -41,11 +41,13 @@ angular.module('contactsApp') }); } ctrl.loading = false; + // Start watching for ctrl.uid when we have addressBooks, as they are needed for fetching + // full details. + $scope.$watch('ctrl.uid', function(newValue) { + ctrl.changeContact(newValue); + }); }); - $scope.$watch('ctrl.uid', function(newValue) { - ctrl.changeContact(newValue); - }); ctrl.changeContact = function(uid) { if (typeof uid === 'undefined') { @@ -53,7 +55,7 @@ angular.module('contactsApp') $('#app-navigation-toggle').removeClass('showdetails'); return; } - ContactService.getById(uid).then(function(contact) { + ContactService.getById(ctrl.addressBooks, uid).then(function(contact) { if (angular.isUndefined(contact)) { ctrl.clearContact(); return; diff --git a/js/components/contactList/contactList_controller.js b/js/components/contactList/contactList_controller.js index a22180d2..bc9e877c 100644 --- a/js/components/contactList/contactList_controller.js +++ b/js/components/contactList/contactList_controller.js @@ -1,5 +1,5 @@ angular.module('contactsApp') -.controller('contactlistCtrl', function($scope, $filter, $route, $routeParams, ContactService, SortByService, vCardPropertiesService, SearchService) { +.controller('contactlistCtrl', function($scope, $filter, $route, $routeParams, $timeout, ContactService, SortByService, vCardPropertiesService, SearchService) { var ctrl = this; ctrl.routeParams = $routeParams; @@ -8,6 +8,7 @@ angular.module('contactsApp') ctrl.searchTerm = ''; ctrl.show = true; ctrl.invalid = false; + ctrl.limitTo = 25; ctrl.sortBy = SortByService.getSortBy(); @@ -15,8 +16,16 @@ angular.module('contactsApp') emptySearch : t('contacts', 'No search result for {query}', {query: ctrl.searchTerm}) }; - $scope.getCountString = function(contacts) { - return n('contacts', '%n contact', '%n contacts', contacts.length); + ctrl.resetLimitTo = function () { + ctrl.limitTo = 25; + clearInterval(ctrl.intervalId); + ctrl.intervalId = setInterval( + function () { + if (!ctrl.loading && ctrl.contacts && ctrl.contacts.length > ctrl.limitTo) { + ctrl.limitTo += 25; + $scope.$apply(); + } + }, 300); }; $scope.query = function(contact) { @@ -34,6 +43,7 @@ angular.module('contactsApp') $scope.$apply(); } if (ev.event === 'changeSearch') { + ctrl.resetLimitTo(); ctrl.searchTerm = ev.searchTerm; ctrl.t.emptySearch = t('contacts', 'No search result for {query}', @@ -46,7 +56,7 @@ angular.module('contactsApp') ctrl.loading = true; ContactService.registerObserverCallback(function(ev) { - $scope.$apply(function() { + $timeout(function () { $scope.$apply(function() { if (ev.event === 'delete') { if (ctrl.contactList.length === 1) { $route.updateParams({ @@ -72,7 +82,7 @@ angular.module('contactsApp') }); } ctrl.contacts = ev.contacts; - }); + }); }); }); // Get contacts @@ -86,7 +96,43 @@ angular.module('contactsApp') } }); - // Wait for ctrl.contactList to be updated, load the first contact and kill the watch + var getVisibleNames = function getVisibleNames() { + function isScrolledIntoView(el) { + var elemTop = el.getBoundingClientRect().top; + var elemBottom = el.getBoundingClientRect().bottom; + + var bothAboveViewport = elemBottom < 0; + var bothBelowViewPort = elemTop > window.innerHeight; + var isVisible = !bothAboveViewport && !bothBelowViewPort; + return isVisible; + } + + var elements = Array.prototype.slice.call(document.querySelectorAll('.contact__icon:not(.ng-hide)')); + var names = elements + .filter(isScrolledIntoView) + .map(function (el) { + var siblings = Array.prototype.slice.call(el.parentElement.children); + var nameElement = siblings.find(function (sibling) { + return sibling.getAttribute('class').indexOf('content-list-item-line-one') !== -1; + }); + return nameElement.innerText; + + }); + return names; + }; + + var timeoutId = null; + document.querySelector('.app-content-list').addEventListener('scroll', function () { + clearTimeout(timeoutId); + timeoutId = setTimeout(function () { + var names = getVisibleNames(); + ContactService.getFullContacts(names); + }, 250); + }); + + // Wait for ctrl.contactList to be updated, load the contact requested in the URL if any, and + // load full details for the probably initially visible contacts. + // Then kill the watch. var unbindListWatch = $scope.$watch('ctrl.contactList', function() { if(ctrl.contactList && ctrl.contactList.length > 0) { // Check if a specific uid is requested @@ -102,6 +148,8 @@ angular.module('contactsApp') if(ctrl.loading && $(window).width() > 768) { ctrl.setSelectedId(ctrl.contactList[0].uid()); } + var firstNames = ctrl.contactList.slice(0, 20).map(function (c) { return c.displayName(); }); + ContactService.getFullContacts(firstNames); ctrl.loading = false; unbindListWatch(); } @@ -142,6 +190,7 @@ angular.module('contactsApp') $scope.$watch('ctrl.routeParams.gid', function() { // we might have to wait until ng-repeat filled the contactList ctrl.contactList = []; + ctrl.resetLimitTo(); // not in mobile mode if($(window).width() > 768) { // watch for next contactList update diff --git a/js/components/group/group_directive.js b/js/components/group/group_directive.js index 3aa5a99f..c1dfdeba 100644 --- a/js/components/group/group_directive.js +++ b/js/components/group/group_directive.js @@ -6,7 +6,8 @@ angular.module('contactsApp') controller: 'groupCtrl', controllerAs: 'ctrl', bindToController: { - group: '=data' + group: '=groupName', + groupCount: '=groupCount' }, templateUrl: OC.linkTo('contacts', 'templates/group.html') }; diff --git a/js/components/groupList/groupList_controller.js b/js/components/groupList/groupList_controller.js index 92548017..417f60a7 100644 --- a/js/components/groupList/groupList_controller.js +++ b/js/components/groupList/groupList_controller.js @@ -2,12 +2,10 @@ angular.module('contactsApp') .controller('grouplistCtrl', function($scope, ContactService, SearchService, $routeParams) { var ctrl = this; - var initialGroups = [t('contacts', 'All contacts'), t('contacts', 'Not grouped')]; + ctrl.groups = []; - ctrl.groups = initialGroups; - - ContactService.getGroups().then(function(groups) { - ctrl.groups = _.unique(initialGroups.concat(groups)); + ContactService.getGroupList().then(function(groups) { + ctrl.groups = groups; }); ctrl.getSelected = function() { @@ -15,12 +13,14 @@ angular.module('contactsApp') }; // Update groupList on contact add/delete/update - ContactService.registerObserverCallback(function() { - $scope.$apply(function() { - ContactService.getGroups().then(function(groups) { - ctrl.groups = _.unique(initialGroups.concat(groups)); + ContactService.registerObserverCallback(function(ev) { + if (ev.event !== 'getFullContacts') { + $scope.$apply(function() { + ContactService.getGroupList().then(function(groups) { + ctrl.groups = groups; + }); }); - }); + } }); ctrl.setSelected = function (selectedGroup) { diff --git a/js/components/newContactButton/newContactButton_controller.js b/js/components/newContactButton/newContactButton_controller.js index 9566974c..c38d8a78 100644 --- a/js/components/newContactButton/newContactButton_controller.js +++ b/js/components/newContactButton/newContactButton_controller.js @@ -13,9 +13,9 @@ angular.module('contactsApp') contact.addProperty(field, defaultValue); } ); if ([t('contacts', 'All contacts'), t('contacts', 'Not grouped')].indexOf($routeParams.gid) === -1) { - contact.categories($routeParams.gid); + contact.categories([ $routeParams.gid ]); } else { - contact.categories(''); + contact.categories([]); } $('#details-fullName').focus(); }); diff --git a/js/dav/dav.js b/js/dav/dav.js index 0e46f394..c01369fb 100644 --- a/js/dav/dav.js +++ b/js/dav/dav.js @@ -938,7 +938,7 @@ exports.createAccount = _co2['default'].wrap(regeneratorRuntime.mark(function ca })); // http redirect. -},{"./calendars":2,"./contacts":5,"./debug":6,"./fuzzy_url_equals":7,"./model":9,"./namespace":10,"./request":12,"co":26,"url":31}],2:[function(require,module,exports){ +},{"./calendars":2,"./contacts":5,"./debug":6,"./fuzzy_url_equals":7,"./model":9,"./namespace":10,"./request":12,"co":32,"url":31}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1329,7 +1329,7 @@ var webdavSync = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$ } }, callee$0$0, this); })); -},{"./debug":6,"./fuzzy_url_equals":7,"./model":9,"./namespace":10,"./request":12,"./webdav":24,"co":26,"url":31}],3:[function(require,module,exports){ +},{"./debug":6,"./fuzzy_url_equals":7,"./model":9,"./namespace":10,"./request":12,"./webdav":25,"co":32,"url":31}],3:[function(require,module,exports){ /** * @fileoverview Camelcase something. */ @@ -1532,6 +1532,14 @@ var Client = (function () { return contacts.deleteCard(card, options); } }, { + key: 'getContacts', + value: function getContacts(addressBook, options, hrefs) { + if (options === undefined) options = {}; + + options.xhr = options.xhr || this.xhr; + return contacts.getContacts(addressBook, options, hrefs); + } + }, { key: 'syncAddressBook', value: function syncAddressBook(addressBook) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; @@ -1743,28 +1751,71 @@ function createCard(addressBook, options) { return webdav.createObject(objectUrl, options.data, options); } +var getFullVcards = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(addressBook, options, hrefs) { + var req, responses; + return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) { + while (1) switch (context$1$0.prev = context$1$0.next) { + case 0: + req = request.addressBookMultiget({ + depth: 1, + props: [{ name: 'getetag', namespace: ns.DAV }, { name: 'address-data', namespace: ns.CARDDAV }], + hrefs: hrefs + }); + context$1$0.next = 3; + return options.xhr.send(req, addressBook.url, { + sandbox: options.sandbox + }); + + case 3: + responses = context$1$0.sent; + return context$1$0.abrupt('return', responses.map(function (res) { + debug('Found vcard with url ' + res.href); + return new _model.VCard({ + data: res, + addressBook: addressBook, + url: _url2['default'].resolve(addressBook.account.rootUrl, res.href), + etag: res.props.getetag, + addressData: res.props.addressData + }); + })); + + case 5: + case 'end': + return context$1$0.stop(); + } + }, callee$0$0, this); +})); + +exports.getFullVcards = getFullVcards; /** * Options: * * (dav.Sandbox) sandbox - optional request sandbox. */ var listVCards = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(addressBook, options) { - var req, responses; + var vCardListFields, req, responses; return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: debug('Doing REPORT on address book ' + addressBook.url + ' which belongs to\n ' + addressBook.account.credentials.username); + vCardListFields = ['EMAIL', 'UID', 'CATEGORIES', 'FN', 'TEL', 'NICKNAME'].map(function (value) { + return { + name: 'prop', + namespace: ns.CARDDAV, + attrs: [{ name: 'name', value: value }] + }; + }); req = request.addressBookQuery({ depth: 1, - props: [{ name: 'getetag', namespace: ns.DAV }, { name: 'address-data', namespace: ns.CARDDAV }] + props: [{ name: 'getetag', namespace: ns.DAV }, { name: 'address-data', namespace: ns.CARDDAV, children: vCardListFields }] }); - context$1$0.next = 4; + context$1$0.next = 5; return options.xhr.send(req, addressBook.url, { sandbox: options.sandbox }); - case 4: + case 5: responses = context$1$0.sent; return context$1$0.abrupt('return', responses.map(function (res) { debug('Found vcard with url ' + res.href); @@ -1777,7 +1828,7 @@ var listVCards = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$ }); })); - case 6: + case 7: case 'end': return context$1$0.stop(); } @@ -1907,6 +1958,9 @@ var syncCarddavAccount = _co2['default'].wrap(regeneratorRuntime.mark(function c })); exports.syncCarddavAccount = syncCarddavAccount; +var getContacts = getFullVcards; + +exports.getContacts = getContacts; var basicSync = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$0(addressBook, options) { var sync; return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) { @@ -1979,7 +2033,7 @@ var webdavSync = _co2['default'].wrap(regeneratorRuntime.mark(function callee$0$ } }, callee$0$0, this); })); -},{"./debug":6,"./fuzzy_url_equals":7,"./model":9,"./namespace":10,"./request":12,"./webdav":24,"co":26,"url":31}],6:[function(require,module,exports){ +},{"./debug":6,"./fuzzy_url_equals":7,"./model":9,"./namespace":10,"./request":12,"./webdav":25,"co":32,"url":31}],6:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -2109,7 +2163,7 @@ exports.debug = _debug2['default']; exports.ns = ns; exports.request = request; exports.transport = transport; -},{"../package":35,"./accounts":1,"./calendars":2,"./client":4,"./contacts":5,"./debug":6,"./model":9,"./namespace":10,"./request":12,"./sandbox":13,"./transport":23}],9:[function(require,module,exports){ +},{"../package":36,"./accounts":1,"./calendars":2,"./client":4,"./contacts":5,"./debug":6,"./model":9,"./namespace":10,"./request":12,"./sandbox":13,"./transport":24}],9:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -2493,13 +2547,14 @@ function children(node, localName) { function child(node, localName) { return children(node, localName)[0]; } -},{"./camelize":3,"./debug":6,"xmldom":32}],12:[function(require,module,exports){ +},{"./camelize":3,"./debug":6,"xmldom":33}],12:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.addressBookQuery = addressBookQuery; +exports.addressBookMultiget = addressBookMultiget; exports.basic = basic; exports.calendarQuery = calendarQuery; exports.collectionQuery = collectionQuery; @@ -2532,6 +2587,10 @@ function addressBookQuery(options) { return collectionQuery(template.addressBookQuery({ props: options.props || [] }), { depth: options.depth }); } +function addressBookMultiget(options) { + return collectionQuery(template.addressBookMultiget({ props: options.props || [], hrefs: options.hrefs || [] }), { depth: options.depth }); +} + /** * Options: * @@ -2758,7 +2817,7 @@ function setRequestHeaders(request, options) { request.setRequestHeader('Overwrite', options.overwrite); } } -},{"./parser":11,"./template":17}],13:[function(require,module,exports){ +},{"./parser":11,"./template":18}],13:[function(require,module,exports){ /** * @fileoverview Group requests together and then abort as a group. * @@ -2818,25 +2877,48 @@ function createSandbox() { return new Sandbox(); } },{"./debug":6}],14:[function(require,module,exports){ -'use strict'; +"use strict"; -Object.defineProperty(exports, '__esModule', { +Object.defineProperty(exports, "__esModule", { value: true }); -exports['default'] = addressBookQuery; +exports["default"] = addressBookMultiget; -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +var _prop = require('./prop'); + +var _prop2 = _interopRequireDefault(_prop); + +function href(href) { + return "<d:href>" + href + "</d:href>"; +} + +function addressBookMultiget(object) { + return "<card:addressbook-multiget xmlns:card=\"urn:ietf:params:xml:ns:carddav\"\n xmlns:d=\"DAV:\">\n <d:prop>\n " + object.props.map(_prop2["default"]).join("") + "\n </d:prop>\n " + object.hrefs.map(href).join("") + "\n </card:addressbook-multiget>"; +} + +module.exports = exports["default"]; +},{"./prop":20}],15:[function(require,module,exports){ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports["default"] = addressBookQuery; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } var _prop = require('./prop'); var _prop2 = _interopRequireDefault(_prop); function addressBookQuery(object) { - return '<card:addressbook-query xmlns:card="urn:ietf:params:xml:ns:carddav"\n xmlns:d="DAV:">\n <d:prop>\n ' + object.props.map(_prop2['default']) + '\n </d:prop>\n <!-- According to http://stackoverflow.com/questions/23742568/google-carddav-api-addressbook-multiget-returns-400-bad-request,\n Google\'s CardDAV server requires a filter element. I don\'t think all addressbook-query calls need a filter in the spec though? -->\n </card:addressbook-query>'; + return "<card:addressbook-query xmlns:card=\"urn:ietf:params:xml:ns:carddav\"\n xmlns:d=\"DAV:\">\n <d:prop>\n " + object.props.map(_prop2["default"]).join("") + "\n </d:prop>\n <!-- According to http://stackoverflow.com/questions/23742568/google-carddav-api-addressbook-multiget-returns-400-bad-request,\n Google's CardDAV server requires a filter element. I don't think all addressbook-query calls need a filter in the spec though? -->\n </card:addressbook-query>"; } -module.exports = exports['default']; -},{"./prop":19}],15:[function(require,module,exports){ +module.exports = exports["default"]; +},{"./prop":20}],16:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -2859,7 +2941,7 @@ function calendarQuery(object) { } module.exports = exports['default']; -},{"./filter":16,"./prop":19}],16:[function(require,module,exports){ +},{"./filter":17,"./prop":20}],17:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -2886,16 +2968,17 @@ function formatAttrs(attrs) { }).join(' '); } module.exports = exports['default']; -},{}],17:[function(require,module,exports){ +},{}],18:[function(require,module,exports){ 'use strict'; exports.addressBookQuery = require('./address_book_query'); +exports.addressBookMultiget = require('./address_book_multiget'); exports.calendarQuery = require('./calendar_query'); exports.propfind = require('./propfind'); exports.syncCollection = require('./sync_collection'); exports.mkcol = require('./mkcol'); exports.proppatch = require('./proppatch'); -},{"./address_book_query":14,"./calendar_query":15,"./mkcol":18,"./propfind":20,"./proppatch":21,"./sync_collection":22}],18:[function(require,module,exports){ +},{"./address_book_multiget":14,"./address_book_query":15,"./calendar_query":16,"./mkcol":19,"./propfind":21,"./proppatch":22,"./sync_collection":23}],19:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -2914,7 +2997,7 @@ function mkcol(object) { } module.exports = exports['default']; -},{"./prop":19}],19:[function(require,module,exports){ +},{"./prop":20}],20:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -2965,15 +3048,23 @@ var ns = _interopRequireWildcard(_namespace); */ function prop(item) { + var tagName = xmlnsPrefix(item.namespace) + ':' + item.name; + var attrs = (item.attrs || []).map(makeAttr).join(' '); if (!item.children || !item.children.length) { if (typeof item.value === "undefined") { - return '<' + xmlnsPrefix(item.namespace) + ':' + item.name + ' />'; + return '<' + tagName + ' ' + attrs + '/>'; } - return '<' + xmlnsPrefix(item.namespace) + ':' + item.name + '>' + item.value + '</' + xmlnsPrefix(item.namespace) + ':' + item.name + '>'; + return '<' + tagName + ' ' + attrs + '>' + item.value + '</' + tagName + '>'; } var children = item.children.map(prop); - return '<' + xmlnsPrefix(item.namespace) + ':' + item.name + '>\n ' + children + '\n </' + xmlnsPrefix(item.namespace) + ':' + item.name + '>'; + return '<' + tagName + ' ' + attrs + '>\n ' + children.join('') + '\n </' + tagName + '>'; +} + +function makeAttr(attr) { + if (!attr.name) return ''; + if (!attr.value) return attr.name; + return attr.name + '="' + attr.value + '"'; } function xmlnsPrefix(namespace) { @@ -2993,7 +3084,7 @@ function xmlnsPrefix(namespace) { } } module.exports = exports['default']; -},{"../namespace":10}],20:[function(require,module,exports){ +},{"../namespace":10}],21:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -3012,7 +3103,7 @@ function propfind(object) { } module.exports = exports['default']; -},{"./prop":19}],21:[function(require,module,exports){ +},{"./prop":20}],22:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -3031,7 +3122,7 @@ function proppatch(object) { } module.exports = exports['default']; -},{"./prop":19}],22:[function(require,module,exports){ +},{"./prop":20}],23:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -3050,7 +3141,7 @@ function syncCollection(object) { } module.exports = exports['default']; -},{"./prop":19}],23:[function(require,module,exports){ +},{"./prop":20}],24:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -3363,7 +3454,7 @@ var refreshAccessToken = _co2['default'].wrap(regeneratorRuntime.mark(function c } }, callee$0$0, this); })); -},{"./xmlhttprequest":25,"co":26,"querystring":30}],24:[function(require,module,exports){ +},{"./xmlhttprequest":26,"co":32,"querystring":30}],25:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -3545,7 +3636,7 @@ var isCollectionDirty = _co2['default'].wrap(regeneratorRuntime.mark(function ca }, callee$0$0, this); })); exports.isCollectionDirty = isCollectionDirty; -},{"./debug":6,"./fuzzy_url_equals":7,"./namespace":10,"./request":12,"co":26}],25:[function(require,module,exports){ +},{"./debug":6,"./fuzzy_url_equals":7,"./namespace":10,"./request":12,"co":32}],26:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -3669,246 +3760,7 @@ var XMLHttpRequest = (function () { exports['default'] = XMLHttpRequest; module.exports = exports['default']; -},{"./debug":6}],26:[function(require,module,exports){ - -/** - * slice() reference. - */ - -var slice = Array.prototype.slice; - -/** - * Expose `co`. - */ - -module.exports = co['default'] = co.co = co; - -/** - * Wrap the given generator `fn` into a - * function that returns a promise. - * This is a separate function so that - * every `co()` call doesn't create a new, - * unnecessary closure. - * - * @param {GeneratorFunction} fn - * @return {Function} - * @api public - */ - -co.wrap = function (fn) { - createPromise.__generatorFunction__ = fn; - return createPromise; - function createPromise() { - return co.call(this, fn.apply(this, arguments)); - } -}; - -/** - * Execute the generator function or a generator - * and return a promise. - * - * @param {Function} fn - * @return {Promise} - * @api public - */ - -function co(gen) { - var ctx = this; - var args = slice.call(arguments, 1) - - // we wrap everything in a promise to avoid promise chaining, - // which leads to memory leak errors. - // see https://github.com/tj/co/issues/180 - return new Promise(function(resolve, reject) { - if (typeof gen === 'function') gen = gen.apply(ctx, args); - if (!gen || typeof gen.next !== 'function') return resolve(gen); - - onFulfilled(); - - /** - * @param {Mixed} res - * @return {Promise} - * @api private - */ - - function onFulfilled(res) { - var ret; - try { - ret = gen.next(res); - } catch (e) { - return reject(e); - } |