diff --git a/api/apiT.js b/api/apiT.js index dce45ef..3848275 100644 --- a/api/apiT.js +++ b/api/apiT.js @@ -1,6 +1,7 @@ // const baseUrl = "https://api.collect.hs.hnzhwlkj.club/"; //这里放置url域名32044 // const baseUrl = "http://192.168.0.72:8083/api/v1/"; //这里放置url域名32044 const baseUrl = "http://219.157.255.213:9007/"; //这里放置url域名32044 + // const baseUrl = "http://192.168.50.4:8787/"; //这里放置url域名32044 // const baseUrl = "/api/"; //这里放置url域名32044 export function request(options){//封装的requset let { url,data,method } = options; diff --git a/common/locales/en.js b/common/locales/en.js index d94a6c0..b0f287e 100644 --- a/common/locales/en.js +++ b/common/locales/en.js @@ -15,7 +15,7 @@ export default { home: { desc: 'home', langbtn:'Change Language', - title:'You have accompanied me', + title:'You have accompanied me LING KE', day:'day', databf:'Data Briefing', addxs:'Add clues', @@ -136,4 +136,7 @@ export default { newpassword:'Password', Confirmpassword:'Confirm password', Confirmbtn:'Confirm modification', + zh:'Chinese', + en:'English', + ru:'Russian' } \ No newline at end of file diff --git a/common/locales/zh.js b/common/locales/zh.js index a5447cd..11a9a90 100644 --- a/common/locales/zh.js +++ b/common/locales/zh.js @@ -14,7 +14,7 @@ export default { home: { desc: '首页', langbtn:'切换语言', - title:'您已陪伴', + title:'您已陪伴领客', day:'天', databf:'数据简报', addxs:'新增线索', @@ -135,4 +135,7 @@ export default { newpassword:'新密码', Confirmpassword:'确认密码', Confirmbtn:'确认修改', + zh:'中文', + en:'英语', + ru:'俄语' } \ No newline at end of file diff --git a/components/userlist/index.vue b/components/userlist/index.vue index b482625..0b7c1ae 100644 --- a/components/userlist/index.vue +++ b/components/userlist/index.vue @@ -5,7 +5,8 @@ {{item.created_at}} {{item.magnitude_txt}} - {{$t('followup')}} + {{item.follow_txt}} + @@ -14,7 +15,7 @@ {{item.contacts}}{{item.phone}} {{item.grade_txt}} - {{item.sex_txt}} | 迪拜{{item.car_type_name}} + {{item.sex_txt}} | {{item.country_name}}{{item.car_type_name}} {{item.describe}} "); + case DOCUMENT_TYPE_NODE: + var pubid = node.publicId; + var sysid = node.systemId; + buf.push(''); + }else if(sysid && sysid!='.'){ + buf.push(' SYSTEM ', sysid, '>'); + }else{ + var sub = node.internalSubset; + if(sub){ + buf.push(" [",sub,"]"); + } + buf.push(">"); + } + return; + case PROCESSING_INSTRUCTION_NODE: + return buf.push( ""); + case ENTITY_REFERENCE_NODE: + return buf.push( '&',node.nodeName,';'); + //case ENTITY_NODE: + //case NOTATION_NODE: + default: + buf.push('??',node.nodeName); + } +} +function importNode(doc,node,deep){ + var node2; + switch (node.nodeType) { + case ELEMENT_NODE: + node2 = node.cloneNode(false); + node2.ownerDocument = doc; + //var attrs = node2.attributes; + //var len = attrs.length; + //for(var i=0;i', + lt: '<', + quot: '"', +}); + +/** + * A map of all entities that are detected in an HTML document. + * They contain all entries from `XML_ENTITIES`. + * + * @see XML_ENTITIES + * @see DOMParser.parseFromString + * @see DOMImplementation.prototype.createHTMLDocument + * @see https://html.spec.whatwg.org/#named-character-references WHATWG HTML(5) Spec + * @see https://html.spec.whatwg.org/entities.json JSON + * @see https://www.w3.org/TR/xml-entity-names/ W3C XML Entity Names + * @see https://www.w3.org/TR/html4/sgml/entities.html W3C HTML4/SGML + * @see https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Character_entity_references_in_HTML Wikipedia (HTML) + * @see https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Entities_representing_special_characters_in_XHTML Wikpedia (XHTML) + */ +exports.HTML_ENTITIES = freeze({ + Aacute: '\u00C1', + aacute: '\u00E1', + Abreve: '\u0102', + abreve: '\u0103', + ac: '\u223E', + acd: '\u223F', + acE: '\u223E\u0333', + Acirc: '\u00C2', + acirc: '\u00E2', + acute: '\u00B4', + Acy: '\u0410', + acy: '\u0430', + AElig: '\u00C6', + aelig: '\u00E6', + af: '\u2061', + Afr: '\uD835\uDD04', + afr: '\uD835\uDD1E', + Agrave: '\u00C0', + agrave: '\u00E0', + alefsym: '\u2135', + aleph: '\u2135', + Alpha: '\u0391', + alpha: '\u03B1', + Amacr: '\u0100', + amacr: '\u0101', + amalg: '\u2A3F', + AMP: '\u0026', + amp: '\u0026', + And: '\u2A53', + and: '\u2227', + andand: '\u2A55', + andd: '\u2A5C', + andslope: '\u2A58', + andv: '\u2A5A', + ang: '\u2220', + ange: '\u29A4', + angle: '\u2220', + angmsd: '\u2221', + angmsdaa: '\u29A8', + angmsdab: '\u29A9', + angmsdac: '\u29AA', + angmsdad: '\u29AB', + angmsdae: '\u29AC', + angmsdaf: '\u29AD', + angmsdag: '\u29AE', + angmsdah: '\u29AF', + angrt: '\u221F', + angrtvb: '\u22BE', + angrtvbd: '\u299D', + angsph: '\u2222', + angst: '\u00C5', + angzarr: '\u237C', + Aogon: '\u0104', + aogon: '\u0105', + Aopf: '\uD835\uDD38', + aopf: '\uD835\uDD52', + ap: '\u2248', + apacir: '\u2A6F', + apE: '\u2A70', + ape: '\u224A', + apid: '\u224B', + apos: '\u0027', + ApplyFunction: '\u2061', + approx: '\u2248', + approxeq: '\u224A', + Aring: '\u00C5', + aring: '\u00E5', + Ascr: '\uD835\uDC9C', + ascr: '\uD835\uDCB6', + Assign: '\u2254', + ast: '\u002A', + asymp: '\u2248', + asympeq: '\u224D', + Atilde: '\u00C3', + atilde: '\u00E3', + Auml: '\u00C4', + auml: '\u00E4', + awconint: '\u2233', + awint: '\u2A11', + backcong: '\u224C', + backepsilon: '\u03F6', + backprime: '\u2035', + backsim: '\u223D', + backsimeq: '\u22CD', + Backslash: '\u2216', + Barv: '\u2AE7', + barvee: '\u22BD', + Barwed: '\u2306', + barwed: '\u2305', + barwedge: '\u2305', + bbrk: '\u23B5', + bbrktbrk: '\u23B6', + bcong: '\u224C', + Bcy: '\u0411', + bcy: '\u0431', + bdquo: '\u201E', + becaus: '\u2235', + Because: '\u2235', + because: '\u2235', + bemptyv: '\u29B0', + bepsi: '\u03F6', + bernou: '\u212C', + Bernoullis: '\u212C', + Beta: '\u0392', + beta: '\u03B2', + beth: '\u2136', + between: '\u226C', + Bfr: '\uD835\uDD05', + bfr: '\uD835\uDD1F', + bigcap: '\u22C2', + bigcirc: '\u25EF', + bigcup: '\u22C3', + bigodot: '\u2A00', + bigoplus: '\u2A01', + bigotimes: '\u2A02', + bigsqcup: '\u2A06', + bigstar: '\u2605', + bigtriangledown: '\u25BD', + bigtriangleup: '\u25B3', + biguplus: '\u2A04', + bigvee: '\u22C1', + bigwedge: '\u22C0', + bkarow: '\u290D', + blacklozenge: '\u29EB', + blacksquare: '\u25AA', + blacktriangle: '\u25B4', + blacktriangledown: '\u25BE', + blacktriangleleft: '\u25C2', + blacktriangleright: '\u25B8', + blank: '\u2423', + blk12: '\u2592', + blk14: '\u2591', + blk34: '\u2593', + block: '\u2588', + bne: '\u003D\u20E5', + bnequiv: '\u2261\u20E5', + bNot: '\u2AED', + bnot: '\u2310', + Bopf: '\uD835\uDD39', + bopf: '\uD835\uDD53', + bot: '\u22A5', + bottom: '\u22A5', + bowtie: '\u22C8', + boxbox: '\u29C9', + boxDL: '\u2557', + boxDl: '\u2556', + boxdL: '\u2555', + boxdl: '\u2510', + boxDR: '\u2554', + boxDr: '\u2553', + boxdR: '\u2552', + boxdr: '\u250C', + boxH: '\u2550', + boxh: '\u2500', + boxHD: '\u2566', + boxHd: '\u2564', + boxhD: '\u2565', + boxhd: '\u252C', + boxHU: '\u2569', + boxHu: '\u2567', + boxhU: '\u2568', + boxhu: '\u2534', + boxminus: '\u229F', + boxplus: '\u229E', + boxtimes: '\u22A0', + boxUL: '\u255D', + boxUl: '\u255C', + boxuL: '\u255B', + boxul: '\u2518', + boxUR: '\u255A', + boxUr: '\u2559', + boxuR: '\u2558', + boxur: '\u2514', + boxV: '\u2551', + boxv: '\u2502', + boxVH: '\u256C', + boxVh: '\u256B', + boxvH: '\u256A', + boxvh: '\u253C', + boxVL: '\u2563', + boxVl: '\u2562', + boxvL: '\u2561', + boxvl: '\u2524', + boxVR: '\u2560', + boxVr: '\u255F', + boxvR: '\u255E', + boxvr: '\u251C', + bprime: '\u2035', + Breve: '\u02D8', + breve: '\u02D8', + brvbar: '\u00A6', + Bscr: '\u212C', + bscr: '\uD835\uDCB7', + bsemi: '\u204F', + bsim: '\u223D', + bsime: '\u22CD', + bsol: '\u005C', + bsolb: '\u29C5', + bsolhsub: '\u27C8', + bull: '\u2022', + bullet: '\u2022', + bump: '\u224E', + bumpE: '\u2AAE', + bumpe: '\u224F', + Bumpeq: '\u224E', + bumpeq: '\u224F', + Cacute: '\u0106', + cacute: '\u0107', + Cap: '\u22D2', + cap: '\u2229', + capand: '\u2A44', + capbrcup: '\u2A49', + capcap: '\u2A4B', + capcup: '\u2A47', + capdot: '\u2A40', + CapitalDifferentialD: '\u2145', + caps: '\u2229\uFE00', + caret: '\u2041', + caron: '\u02C7', + Cayleys: '\u212D', + ccaps: '\u2A4D', + Ccaron: '\u010C', + ccaron: '\u010D', + Ccedil: '\u00C7', + ccedil: '\u00E7', + Ccirc: '\u0108', + ccirc: '\u0109', + Cconint: '\u2230', + ccups: '\u2A4C', + ccupssm: '\u2A50', + Cdot: '\u010A', + cdot: '\u010B', + cedil: '\u00B8', + Cedilla: '\u00B8', + cemptyv: '\u29B2', + cent: '\u00A2', + CenterDot: '\u00B7', + centerdot: '\u00B7', + Cfr: '\u212D', + cfr: '\uD835\uDD20', + CHcy: '\u0427', + chcy: '\u0447', + check: '\u2713', + checkmark: '\u2713', + Chi: '\u03A7', + chi: '\u03C7', + cir: '\u25CB', + circ: '\u02C6', + circeq: '\u2257', + circlearrowleft: '\u21BA', + circlearrowright: '\u21BB', + circledast: '\u229B', + circledcirc: '\u229A', + circleddash: '\u229D', + CircleDot: '\u2299', + circledR: '\u00AE', + circledS: '\u24C8', + CircleMinus: '\u2296', + CirclePlus: '\u2295', + CircleTimes: '\u2297', + cirE: '\u29C3', + cire: '\u2257', + cirfnint: '\u2A10', + cirmid: '\u2AEF', + cirscir: '\u29C2', + ClockwiseContourIntegral: '\u2232', + CloseCurlyDoubleQuote: '\u201D', + CloseCurlyQuote: '\u2019', + clubs: '\u2663', + clubsuit: '\u2663', + Colon: '\u2237', + colon: '\u003A', + Colone: '\u2A74', + colone: '\u2254', + coloneq: '\u2254', + comma: '\u002C', + commat: '\u0040', + comp: '\u2201', + compfn: '\u2218', + complement: '\u2201', + complexes: '\u2102', + cong: '\u2245', + congdot: '\u2A6D', + Congruent: '\u2261', + Conint: '\u222F', + conint: '\u222E', + ContourIntegral: '\u222E', + Copf: '\u2102', + copf: '\uD835\uDD54', + coprod: '\u2210', + Coproduct: '\u2210', + COPY: '\u00A9', + copy: '\u00A9', + copysr: '\u2117', + CounterClockwiseContourIntegral: '\u2233', + crarr: '\u21B5', + Cross: '\u2A2F', + cross: '\u2717', + Cscr: '\uD835\uDC9E', + cscr: '\uD835\uDCB8', + csub: '\u2ACF', + csube: '\u2AD1', + csup: '\u2AD0', + csupe: '\u2AD2', + ctdot: '\u22EF', + cudarrl: '\u2938', + cudarrr: '\u2935', + cuepr: '\u22DE', + cuesc: '\u22DF', + cularr: '\u21B6', + cularrp: '\u293D', + Cup: '\u22D3', + cup: '\u222A', + cupbrcap: '\u2A48', + CupCap: '\u224D', + cupcap: '\u2A46', + cupcup: '\u2A4A', + cupdot: '\u228D', + cupor: '\u2A45', + cups: '\u222A\uFE00', + curarr: '\u21B7', + curarrm: '\u293C', + curlyeqprec: '\u22DE', + curlyeqsucc: '\u22DF', + curlyvee: '\u22CE', + curlywedge: '\u22CF', + curren: '\u00A4', + curvearrowleft: '\u21B6', + curvearrowright: '\u21B7', + cuvee: '\u22CE', + cuwed: '\u22CF', + cwconint: '\u2232', + cwint: '\u2231', + cylcty: '\u232D', + Dagger: '\u2021', + dagger: '\u2020', + daleth: '\u2138', + Darr: '\u21A1', + dArr: '\u21D3', + darr: '\u2193', + dash: '\u2010', + Dashv: '\u2AE4', + dashv: '\u22A3', + dbkarow: '\u290F', + dblac: '\u02DD', + Dcaron: '\u010E', + dcaron: '\u010F', + Dcy: '\u0414', + dcy: '\u0434', + DD: '\u2145', + dd: '\u2146', + ddagger: '\u2021', + ddarr: '\u21CA', + DDotrahd: '\u2911', + ddotseq: '\u2A77', + deg: '\u00B0', + Del: '\u2207', + Delta: '\u0394', + delta: '\u03B4', + demptyv: '\u29B1', + dfisht: '\u297F', + Dfr: '\uD835\uDD07', + dfr: '\uD835\uDD21', + dHar: '\u2965', + dharl: '\u21C3', + dharr: '\u21C2', + DiacriticalAcute: '\u00B4', + DiacriticalDot: '\u02D9', + DiacriticalDoubleAcute: '\u02DD', + DiacriticalGrave: '\u0060', + DiacriticalTilde: '\u02DC', + diam: '\u22C4', + Diamond: '\u22C4', + diamond: '\u22C4', + diamondsuit: '\u2666', + diams: '\u2666', + die: '\u00A8', + DifferentialD: '\u2146', + digamma: '\u03DD', + disin: '\u22F2', + div: '\u00F7', + divide: '\u00F7', + divideontimes: '\u22C7', + divonx: '\u22C7', + DJcy: '\u0402', + djcy: '\u0452', + dlcorn: '\u231E', + dlcrop: '\u230D', + dollar: '\u0024', + Dopf: '\uD835\uDD3B', + dopf: '\uD835\uDD55', + Dot: '\u00A8', + dot: '\u02D9', + DotDot: '\u20DC', + doteq: '\u2250', + doteqdot: '\u2251', + DotEqual: '\u2250', + dotminus: '\u2238', + dotplus: '\u2214', + dotsquare: '\u22A1', + doublebarwedge: '\u2306', + DoubleContourIntegral: '\u222F', + DoubleDot: '\u00A8', + DoubleDownArrow: '\u21D3', + DoubleLeftArrow: '\u21D0', + DoubleLeftRightArrow: '\u21D4', + DoubleLeftTee: '\u2AE4', + DoubleLongLeftArrow: '\u27F8', + DoubleLongLeftRightArrow: '\u27FA', + DoubleLongRightArrow: '\u27F9', + DoubleRightArrow: '\u21D2', + DoubleRightTee: '\u22A8', + DoubleUpArrow: '\u21D1', + DoubleUpDownArrow: '\u21D5', + DoubleVerticalBar: '\u2225', + DownArrow: '\u2193', + Downarrow: '\u21D3', + downarrow: '\u2193', + DownArrowBar: '\u2913', + DownArrowUpArrow: '\u21F5', + DownBreve: '\u0311', + downdownarrows: '\u21CA', + downharpoonleft: '\u21C3', + downharpoonright: '\u21C2', + DownLeftRightVector: '\u2950', + DownLeftTeeVector: '\u295E', + DownLeftVector: '\u21BD', + DownLeftVectorBar: '\u2956', + DownRightTeeVector: '\u295F', + DownRightVector: '\u21C1', + DownRightVectorBar: '\u2957', + DownTee: '\u22A4', + DownTeeArrow: '\u21A7', + drbkarow: '\u2910', + drcorn: '\u231F', + drcrop: '\u230C', + Dscr: '\uD835\uDC9F', + dscr: '\uD835\uDCB9', + DScy: '\u0405', + dscy: '\u0455', + dsol: '\u29F6', + Dstrok: '\u0110', + dstrok: '\u0111', + dtdot: '\u22F1', + dtri: '\u25BF', + dtrif: '\u25BE', + duarr: '\u21F5', + duhar: '\u296F', + dwangle: '\u29A6', + DZcy: '\u040F', + dzcy: '\u045F', + dzigrarr: '\u27FF', + Eacute: '\u00C9', + eacute: '\u00E9', + easter: '\u2A6E', + Ecaron: '\u011A', + ecaron: '\u011B', + ecir: '\u2256', + Ecirc: '\u00CA', + ecirc: '\u00EA', + ecolon: '\u2255', + Ecy: '\u042D', + ecy: '\u044D', + eDDot: '\u2A77', + Edot: '\u0116', + eDot: '\u2251', + edot: '\u0117', + ee: '\u2147', + efDot: '\u2252', + Efr: '\uD835\uDD08', + efr: '\uD835\uDD22', + eg: '\u2A9A', + Egrave: '\u00C8', + egrave: '\u00E8', + egs: '\u2A96', + egsdot: '\u2A98', + el: '\u2A99', + Element: '\u2208', + elinters: '\u23E7', + ell: '\u2113', + els: '\u2A95', + elsdot: '\u2A97', + Emacr: '\u0112', + emacr: '\u0113', + empty: '\u2205', + emptyset: '\u2205', + EmptySmallSquare: '\u25FB', + emptyv: '\u2205', + EmptyVerySmallSquare: '\u25AB', + emsp: '\u2003', + emsp13: '\u2004', + emsp14: '\u2005', + ENG: '\u014A', + eng: '\u014B', + ensp: '\u2002', + Eogon: '\u0118', + eogon: '\u0119', + Eopf: '\uD835\uDD3C', + eopf: '\uD835\uDD56', + epar: '\u22D5', + eparsl: '\u29E3', + eplus: '\u2A71', + epsi: '\u03B5', + Epsilon: '\u0395', + epsilon: '\u03B5', + epsiv: '\u03F5', + eqcirc: '\u2256', + eqcolon: '\u2255', + eqsim: '\u2242', + eqslantgtr: '\u2A96', + eqslantless: '\u2A95', + Equal: '\u2A75', + equals: '\u003D', + EqualTilde: '\u2242', + equest: '\u225F', + Equilibrium: '\u21CC', + equiv: '\u2261', + equivDD: '\u2A78', + eqvparsl: '\u29E5', + erarr: '\u2971', + erDot: '\u2253', + Escr: '\u2130', + escr: '\u212F', + esdot: '\u2250', + Esim: '\u2A73', + esim: '\u2242', + Eta: '\u0397', + eta: '\u03B7', + ETH: '\u00D0', + eth: '\u00F0', + Euml: '\u00CB', + euml: '\u00EB', + euro: '\u20AC', + excl: '\u0021', + exist: '\u2203', + Exists: '\u2203', + expectation: '\u2130', + ExponentialE: '\u2147', + exponentiale: '\u2147', + fallingdotseq: '\u2252', + Fcy: '\u0424', + fcy: '\u0444', + female: '\u2640', + ffilig: '\uFB03', + fflig: '\uFB00', + ffllig: '\uFB04', + Ffr: '\uD835\uDD09', + ffr: '\uD835\uDD23', + filig: '\uFB01', + FilledSmallSquare: '\u25FC', + FilledVerySmallSquare: '\u25AA', + fjlig: '\u0066\u006A', + flat: '\u266D', + fllig: '\uFB02', + fltns: '\u25B1', + fnof: '\u0192', + Fopf: '\uD835\uDD3D', + fopf: '\uD835\uDD57', + ForAll: '\u2200', + forall: '\u2200', + fork: '\u22D4', + forkv: '\u2AD9', + Fouriertrf: '\u2131', + fpartint: '\u2A0D', + frac12: '\u00BD', + frac13: '\u2153', + frac14: '\u00BC', + frac15: '\u2155', + frac16: '\u2159', + frac18: '\u215B', + frac23: '\u2154', + frac25: '\u2156', + frac34: '\u00BE', + frac35: '\u2157', + frac38: '\u215C', + frac45: '\u2158', + frac56: '\u215A', + frac58: '\u215D', + frac78: '\u215E', + frasl: '\u2044', + frown: '\u2322', + Fscr: '\u2131', + fscr: '\uD835\uDCBB', + gacute: '\u01F5', + Gamma: '\u0393', + gamma: '\u03B3', + Gammad: '\u03DC', + gammad: '\u03DD', + gap: '\u2A86', + Gbreve: '\u011E', + gbreve: '\u011F', + Gcedil: '\u0122', + Gcirc: '\u011C', + gcirc: '\u011D', + Gcy: '\u0413', + gcy: '\u0433', + Gdot: '\u0120', + gdot: '\u0121', + gE: '\u2267', + ge: '\u2265', + gEl: '\u2A8C', + gel: '\u22DB', + geq: '\u2265', + geqq: '\u2267', + geqslant: '\u2A7E', + ges: '\u2A7E', + gescc: '\u2AA9', + gesdot: '\u2A80', + gesdoto: '\u2A82', + gesdotol: '\u2A84', + gesl: '\u22DB\uFE00', + gesles: '\u2A94', + Gfr: '\uD835\uDD0A', + gfr: '\uD835\uDD24', + Gg: '\u22D9', + gg: '\u226B', + ggg: '\u22D9', + gimel: '\u2137', + GJcy: '\u0403', + gjcy: '\u0453', + gl: '\u2277', + gla: '\u2AA5', + glE: '\u2A92', + glj: '\u2AA4', + gnap: '\u2A8A', + gnapprox: '\u2A8A', + gnE: '\u2269', + gne: '\u2A88', + gneq: '\u2A88', + gneqq: '\u2269', + gnsim: '\u22E7', + Gopf: '\uD835\uDD3E', + gopf: '\uD835\uDD58', + grave: '\u0060', + GreaterEqual: '\u2265', + GreaterEqualLess: '\u22DB', + GreaterFullEqual: '\u2267', + GreaterGreater: '\u2AA2', + GreaterLess: '\u2277', + GreaterSlantEqual: '\u2A7E', + GreaterTilde: '\u2273', + Gscr: '\uD835\uDCA2', + gscr: '\u210A', + gsim: '\u2273', + gsime: '\u2A8E', + gsiml: '\u2A90', + Gt: '\u226B', + GT: '\u003E', + gt: '\u003E', + gtcc: '\u2AA7', + gtcir: '\u2A7A', + gtdot: '\u22D7', + gtlPar: '\u2995', + gtquest: '\u2A7C', + gtrapprox: '\u2A86', + gtrarr: '\u2978', + gtrdot: '\u22D7', + gtreqless: '\u22DB', + gtreqqless: '\u2A8C', + gtrless: '\u2277', + gtrsim: '\u2273', + gvertneqq: '\u2269\uFE00', + gvnE: '\u2269\uFE00', + Hacek: '\u02C7', + hairsp: '\u200A', + half: '\u00BD', + hamilt: '\u210B', + HARDcy: '\u042A', + hardcy: '\u044A', + hArr: '\u21D4', + harr: '\u2194', + harrcir: '\u2948', + harrw: '\u21AD', + Hat: '\u005E', + hbar: '\u210F', + Hcirc: '\u0124', + hcirc: '\u0125', + hearts: '\u2665', + heartsuit: '\u2665', + hellip: '\u2026', + hercon: '\u22B9', + Hfr: '\u210C', + hfr: '\uD835\uDD25', + HilbertSpace: '\u210B', + hksearow: '\u2925', + hkswarow: '\u2926', + hoarr: '\u21FF', + homtht: '\u223B', + hookleftarrow: '\u21A9', + hookrightarrow: '\u21AA', + Hopf: '\u210D', + hopf: '\uD835\uDD59', + horbar: '\u2015', + HorizontalLine: '\u2500', + Hscr: '\u210B', + hscr: '\uD835\uDCBD', + hslash: '\u210F', + Hstrok: '\u0126', + hstrok: '\u0127', + HumpDownHump: '\u224E', + HumpEqual: '\u224F', + hybull: '\u2043', + hyphen: '\u2010', + Iacute: '\u00CD', + iacute: '\u00ED', + ic: '\u2063', + Icirc: '\u00CE', + icirc: '\u00EE', + Icy: '\u0418', + icy: '\u0438', + Idot: '\u0130', + IEcy: '\u0415', + iecy: '\u0435', + iexcl: '\u00A1', + iff: '\u21D4', + Ifr: '\u2111', + ifr: '\uD835\uDD26', + Igrave: '\u00CC', + igrave: '\u00EC', + ii: '\u2148', + iiiint: '\u2A0C', + iiint: '\u222D', + iinfin: '\u29DC', + iiota: '\u2129', + IJlig: '\u0132', + ijlig: '\u0133', + Im: '\u2111', + Imacr: '\u012A', + imacr: '\u012B', + image: '\u2111', + ImaginaryI: '\u2148', + imagline: '\u2110', + imagpart: '\u2111', + imath: '\u0131', + imof: '\u22B7', + imped: '\u01B5', + Implies: '\u21D2', + in: '\u2208', + incare: '\u2105', + infin: '\u221E', + infintie: '\u29DD', + inodot: '\u0131', + Int: '\u222C', + int: '\u222B', + intcal: '\u22BA', + integers: '\u2124', + Integral: '\u222B', + intercal: '\u22BA', + Intersection: '\u22C2', + intlarhk: '\u2A17', + intprod: '\u2A3C', + InvisibleComma: '\u2063', + InvisibleTimes: '\u2062', + IOcy: '\u0401', + iocy: '\u0451', + Iogon: '\u012E', + iogon: '\u012F', + Iopf: '\uD835\uDD40', + iopf: '\uD835\uDD5A', + Iota: '\u0399', + iota: '\u03B9', + iprod: '\u2A3C', + iquest: '\u00BF', + Iscr: '\u2110', + iscr: '\uD835\uDCBE', + isin: '\u2208', + isindot: '\u22F5', + isinE: '\u22F9', + isins: '\u22F4', + isinsv: '\u22F3', + isinv: '\u2208', + it: '\u2062', + Itilde: '\u0128', + itilde: '\u0129', + Iukcy: '\u0406', + iukcy: '\u0456', + Iuml: '\u00CF', + iuml: '\u00EF', + Jcirc: '\u0134', + jcirc: '\u0135', + Jcy: '\u0419', + jcy: '\u0439', + Jfr: '\uD835\uDD0D', + jfr: '\uD835\uDD27', + jmath: '\u0237', + Jopf: '\uD835\uDD41', + jopf: '\uD835\uDD5B', + Jscr: '\uD835\uDCA5', + jscr: '\uD835\uDCBF', + Jsercy: '\u0408', + jsercy: '\u0458', + Jukcy: '\u0404', + jukcy: '\u0454', + Kappa: '\u039A', + kappa: '\u03BA', + kappav: '\u03F0', + Kcedil: '\u0136', + kcedil: '\u0137', + Kcy: '\u041A', + kcy: '\u043A', + Kfr: '\uD835\uDD0E', + kfr: '\uD835\uDD28', + kgreen: '\u0138', + KHcy: '\u0425', + khcy: '\u0445', + KJcy: '\u040C', + kjcy: '\u045C', + Kopf: '\uD835\uDD42', + kopf: '\uD835\uDD5C', + Kscr: '\uD835\uDCA6', + kscr: '\uD835\uDCC0', + lAarr: '\u21DA', + Lacute: '\u0139', + lacute: '\u013A', + laemptyv: '\u29B4', + lagran: '\u2112', + Lambda: '\u039B', + lambda: '\u03BB', + Lang: '\u27EA', + lang: '\u27E8', + langd: '\u2991', + langle: '\u27E8', + lap: '\u2A85', + Laplacetrf: '\u2112', + laquo: '\u00AB', + Larr: '\u219E', + lArr: '\u21D0', + larr: '\u2190', + larrb: '\u21E4', + larrbfs: '\u291F', + larrfs: '\u291D', + larrhk: '\u21A9', + larrlp: '\u21AB', + larrpl: '\u2939', + larrsim: '\u2973', + larrtl: '\u21A2', + lat: '\u2AAB', + lAtail: '\u291B', + latail: '\u2919', + late: '\u2AAD', + lates: '\u2AAD\uFE00', + lBarr: '\u290E', + lbarr: '\u290C', + lbbrk: '\u2772', + lbrace: '\u007B', + lbrack: '\u005B', + lbrke: '\u298B', + lbrksld: '\u298F', + lbrkslu: '\u298D', + Lcaron: '\u013D', + lcaron: '\u013E', + Lcedil: '\u013B', + lcedil: '\u013C', + lceil: '\u2308', + lcub: '\u007B', + Lcy: '\u041B', + lcy: '\u043B', + ldca: '\u2936', + ldquo: '\u201C', + ldquor: '\u201E', + ldrdhar: '\u2967', + ldrushar: '\u294B', + ldsh: '\u21B2', + lE: '\u2266', + le: '\u2264', + LeftAngleBracket: '\u27E8', + LeftArrow: '\u2190', + Leftarrow: '\u21D0', + leftarrow: '\u2190', + LeftArrowBar: '\u21E4', + LeftArrowRightArrow: '\u21C6', + leftarrowtail: '\u21A2', + LeftCeiling: '\u2308', + LeftDoubleBracket: '\u27E6', + LeftDownTeeVector: '\u2961', + LeftDownVector: '\u21C3', + LeftDownVectorBar: '\u2959', + LeftFloor: '\u230A', + leftharpoondown: '\u21BD', + leftharpoonup: '\u21BC', + leftleftarrows: '\u21C7', + LeftRightArrow: '\u2194', + Leftrightarrow: '\u21D4', + leftrightarrow: '\u2194', + leftrightarrows: '\u21C6', + leftrightharpoons: '\u21CB', + leftrightsquigarrow: '\u21AD', + LeftRightVector: '\u294E', + LeftTee: '\u22A3', + LeftTeeArrow: '\u21A4', + LeftTeeVector: '\u295A', + leftthreetimes: '\u22CB', + LeftTriangle: '\u22B2', + LeftTriangleBar: '\u29CF', + LeftTriangleEqual: '\u22B4', + LeftUpDownVector: '\u2951', + LeftUpTeeVector: '\u2960', + LeftUpVector: '\u21BF', + LeftUpVectorBar: '\u2958', + LeftVector: '\u21BC', + LeftVectorBar: '\u2952', + lEg: '\u2A8B', + leg: '\u22DA', + leq: '\u2264', + leqq: '\u2266', + leqslant: '\u2A7D', + les: '\u2A7D', + lescc: '\u2AA8', + lesdot: '\u2A7F', + lesdoto: '\u2A81', + lesdotor: '\u2A83', + lesg: '\u22DA\uFE00', + lesges: '\u2A93', + lessapprox: '\u2A85', + lessdot: '\u22D6', + lesseqgtr: '\u22DA', + lesseqqgtr: '\u2A8B', + LessEqualGreater: '\u22DA', + LessFullEqual: '\u2266', + LessGreater: '\u2276', + lessgtr: '\u2276', + LessLess: '\u2AA1', + lesssim: '\u2272', + LessSlantEqual: '\u2A7D', + LessTilde: '\u2272', + lfisht: '\u297C', + lfloor: '\u230A', + Lfr: '\uD835\uDD0F', + lfr: '\uD835\uDD29', + lg: '\u2276', + lgE: '\u2A91', + lHar: '\u2962', + lhard: '\u21BD', + lharu: '\u21BC', + lharul: '\u296A', + lhblk: '\u2584', + LJcy: '\u0409', + ljcy: '\u0459', + Ll: '\u22D8', + ll: '\u226A', + llarr: '\u21C7', + llcorner: '\u231E', + Lleftarrow: '\u21DA', + llhard: '\u296B', + lltri: '\u25FA', + Lmidot: '\u013F', + lmidot: '\u0140', + lmoust: '\u23B0', + lmoustache: '\u23B0', + lnap: '\u2A89', + lnapprox: '\u2A89', + lnE: '\u2268', + lne: '\u2A87', + lneq: '\u2A87', + lneqq: '\u2268', + lnsim: '\u22E6', + loang: '\u27EC', + loarr: '\u21FD', + lobrk: '\u27E6', + LongLeftArrow: '\u27F5', + Longleftarrow: '\u27F8', + longleftarrow: '\u27F5', + LongLeftRightArrow: '\u27F7', + Longleftrightarrow: '\u27FA', + longleftrightarrow: '\u27F7', + longmapsto: '\u27FC', + LongRightArrow: '\u27F6', + Longrightarrow: '\u27F9', + longrightarrow: '\u27F6', + looparrowleft: '\u21AB', + looparrowright: '\u21AC', + lopar: '\u2985', + Lopf: '\uD835\uDD43', + lopf: '\uD835\uDD5D', + loplus: '\u2A2D', + lotimes: '\u2A34', + lowast: '\u2217', + lowbar: '\u005F', + LowerLeftArrow: '\u2199', + LowerRightArrow: '\u2198', + loz: '\u25CA', + lozenge: '\u25CA', + lozf: '\u29EB', + lpar: '\u0028', + lparlt: '\u2993', + lrarr: '\u21C6', + lrcorner: '\u231F', + lrhar: '\u21CB', + lrhard: '\u296D', + lrm: '\u200E', + lrtri: '\u22BF', + lsaquo: '\u2039', + Lscr: '\u2112', + lscr: '\uD835\uDCC1', + Lsh: '\u21B0', + lsh: '\u21B0', + lsim: '\u2272', + lsime: '\u2A8D', + lsimg: '\u2A8F', + lsqb: '\u005B', + lsquo: '\u2018', + lsquor: '\u201A', + Lstrok: '\u0141', + lstrok: '\u0142', + Lt: '\u226A', + LT: '\u003C', + lt: '\u003C', + ltcc: '\u2AA6', + ltcir: '\u2A79', + ltdot: '\u22D6', + lthree: '\u22CB', + ltimes: '\u22C9', + ltlarr: '\u2976', + ltquest: '\u2A7B', + ltri: '\u25C3', + ltrie: '\u22B4', + ltrif: '\u25C2', + ltrPar: '\u2996', + lurdshar: '\u294A', + luruhar: '\u2966', + lvertneqq: '\u2268\uFE00', + lvnE: '\u2268\uFE00', + macr: '\u00AF', + male: '\u2642', + malt: '\u2720', + maltese: '\u2720', + Map: '\u2905', + map: '\u21A6', + mapsto: '\u21A6', + mapstodown: '\u21A7', + mapstoleft: '\u21A4', + mapstoup: '\u21A5', + marker: '\u25AE', + mcomma: '\u2A29', + Mcy: '\u041C', + mcy: '\u043C', + mdash: '\u2014', + mDDot: '\u223A', + measuredangle: '\u2221', + MediumSpace: '\u205F', + Mellintrf: '\u2133', + Mfr: '\uD835\uDD10', + mfr: '\uD835\uDD2A', + mho: '\u2127', + micro: '\u00B5', + mid: '\u2223', + midast: '\u002A', + midcir: '\u2AF0', + middot: '\u00B7', + minus: '\u2212', + minusb: '\u229F', + minusd: '\u2238', + minusdu: '\u2A2A', + MinusPlus: '\u2213', + mlcp: '\u2ADB', + mldr: '\u2026', + mnplus: '\u2213', + models: '\u22A7', + Mopf: '\uD835\uDD44', + mopf: '\uD835\uDD5E', + mp: '\u2213', + Mscr: '\u2133', + mscr: '\uD835\uDCC2', + mstpos: '\u223E', + Mu: '\u039C', + mu: '\u03BC', + multimap: '\u22B8', + mumap: '\u22B8', + nabla: '\u2207', + Nacute: '\u0143', + nacute: '\u0144', + nang: '\u2220\u20D2', + nap: '\u2249', + napE: '\u2A70\u0338', + napid: '\u224B\u0338', + napos: '\u0149', + napprox: '\u2249', + natur: '\u266E', + natural: '\u266E', + naturals: '\u2115', + nbsp: '\u00A0', + nbump: '\u224E\u0338', + nbumpe: '\u224F\u0338', + ncap: '\u2A43', + Ncaron: '\u0147', + ncaron: '\u0148', + Ncedil: '\u0145', + ncedil: '\u0146', + ncong: '\u2247', + ncongdot: '\u2A6D\u0338', + ncup: '\u2A42', + Ncy: '\u041D', + ncy: '\u043D', + ndash: '\u2013', + ne: '\u2260', + nearhk: '\u2924', + neArr: '\u21D7', + nearr: '\u2197', + nearrow: '\u2197', + nedot: '\u2250\u0338', + NegativeMediumSpace: '\u200B', + NegativeThickSpace: '\u200B', + NegativeThinSpace: '\u200B', + NegativeVeryThinSpace: '\u200B', + nequiv: '\u2262', + nesear: '\u2928', + nesim: '\u2242\u0338', + NestedGreaterGreater: '\u226B', + NestedLessLess: '\u226A', + NewLine: '\u000A', + nexist: '\u2204', + nexists: '\u2204', + Nfr: '\uD835\uDD11', + nfr: '\uD835\uDD2B', + ngE: '\u2267\u0338', + nge: '\u2271', + ngeq: '\u2271', + ngeqq: '\u2267\u0338', + ngeqslant: '\u2A7E\u0338', + nges: '\u2A7E\u0338', + nGg: '\u22D9\u0338', + ngsim: '\u2275', + nGt: '\u226B\u20D2', + ngt: '\u226F', + ngtr: '\u226F', + nGtv: '\u226B\u0338', + nhArr: '\u21CE', + nharr: '\u21AE', + nhpar: '\u2AF2', + ni: '\u220B', + nis: '\u22FC', + nisd: '\u22FA', + niv: '\u220B', + NJcy: '\u040A', + njcy: '\u045A', + nlArr: '\u21CD', + nlarr: '\u219A', + nldr: '\u2025', + nlE: '\u2266\u0338', + nle: '\u2270', + nLeftarrow: '\u21CD', + nleftarrow: '\u219A', + nLeftrightarrow: '\u21CE', + nleftrightarrow: '\u21AE', + nleq: '\u2270', + nleqq: '\u2266\u0338', + nleqslant: '\u2A7D\u0338', + nles: '\u2A7D\u0338', + nless: '\u226E', + nLl: '\u22D8\u0338', + nlsim: '\u2274', + nLt: '\u226A\u20D2', + nlt: '\u226E', + nltri: '\u22EA', + nltrie: '\u22EC', + nLtv: '\u226A\u0338', + nmid: '\u2224', + NoBreak: '\u2060', + NonBreakingSpace: '\u00A0', + Nopf: '\u2115', + nopf: '\uD835\uDD5F', + Not: '\u2AEC', + not: '\u00AC', + NotCongruent: '\u2262', + NotCupCap: '\u226D', + NotDoubleVerticalBar: '\u2226', + NotElement: '\u2209', + NotEqual: '\u2260', + NotEqualTilde: '\u2242\u0338', + NotExists: '\u2204', + NotGreater: '\u226F', + NotGreaterEqual: '\u2271', + NotGreaterFullEqual: '\u2267\u0338', + NotGreaterGreater: '\u226B\u0338', + NotGreaterLess: '\u2279', + NotGreaterSlantEqual: '\u2A7E\u0338', + NotGreaterTilde: '\u2275', + NotHumpDownHump: '\u224E\u0338', + NotHumpEqual: '\u224F\u0338', + notin: '\u2209', + notindot: '\u22F5\u0338', + notinE: '\u22F9\u0338', + notinva: '\u2209', + notinvb: '\u22F7', + notinvc: '\u22F6', + NotLeftTriangle: '\u22EA', + NotLeftTriangleBar: '\u29CF\u0338', + NotLeftTriangleEqual: '\u22EC', + NotLess: '\u226E', + NotLessEqual: '\u2270', + NotLessGreater: '\u2278', + NotLessLess: '\u226A\u0338', + NotLessSlantEqual: '\u2A7D\u0338', + NotLessTilde: '\u2274', + NotNestedGreaterGreater: '\u2AA2\u0338', + NotNestedLessLess: '\u2AA1\u0338', + notni: '\u220C', + notniva: '\u220C', + notnivb: '\u22FE', + notnivc: '\u22FD', + NotPrecedes: '\u2280', + NotPrecedesEqual: '\u2AAF\u0338', + NotPrecedesSlantEqual: '\u22E0', + NotReverseElement: '\u220C', + NotRightTriangle: '\u22EB', + NotRightTriangleBar: '\u29D0\u0338', + NotRightTriangleEqual: '\u22ED', + NotSquareSubset: '\u228F\u0338', + NotSquareSubsetEqual: '\u22E2', + NotSquareSuperset: '\u2290\u0338', + NotSquareSupersetEqual: '\u22E3', + NotSubset: '\u2282\u20D2', + NotSubsetEqual: '\u2288', + NotSucceeds: '\u2281', + NotSucceedsEqual: '\u2AB0\u0338', + NotSucceedsSlantEqual: '\u22E1', + NotSucceedsTilde: '\u227F\u0338', + NotSuperset: '\u2283\u20D2', + NotSupersetEqual: '\u2289', + NotTilde: '\u2241', + NotTildeEqual: '\u2244', + NotTildeFullEqual: '\u2247', + NotTildeTilde: '\u2249', + NotVerticalBar: '\u2224', + npar: '\u2226', + nparallel: '\u2226', + nparsl: '\u2AFD\u20E5', + npart: '\u2202\u0338', + npolint: '\u2A14', + npr: '\u2280', + nprcue: '\u22E0', + npre: '\u2AAF\u0338', + nprec: '\u2280', + npreceq: '\u2AAF\u0338', + nrArr: '\u21CF', + nrarr: '\u219B', + nrarrc: '\u2933\u0338', + nrarrw: '\u219D\u0338', + nRightarrow: '\u21CF', + nrightarrow: '\u219B', + nrtri: '\u22EB', + nrtrie: '\u22ED', + nsc: '\u2281', + nsccue: '\u22E1', + nsce: '\u2AB0\u0338', + Nscr: '\uD835\uDCA9', + nscr: '\uD835\uDCC3', + nshortmid: '\u2224', + nshortparallel: '\u2226', + nsim: '\u2241', + nsime: '\u2244', + nsimeq: '\u2244', + nsmid: '\u2224', + nspar: '\u2226', + nsqsube: '\u22E2', + nsqsupe: '\u22E3', + nsub: '\u2284', + nsubE: '\u2AC5\u0338', + nsube: '\u2288', + nsubset: '\u2282\u20D2', + nsubseteq: '\u2288', + nsubseteqq: '\u2AC5\u0338', + nsucc: '\u2281', + nsucceq: '\u2AB0\u0338', + nsup: '\u2285', + nsupE: '\u2AC6\u0338', + nsupe: '\u2289', + nsupset: '\u2283\u20D2', + nsupseteq: '\u2289', + nsupseteqq: '\u2AC6\u0338', + ntgl: '\u2279', + Ntilde: '\u00D1', + ntilde: '\u00F1', + ntlg: '\u2278', + ntriangleleft: '\u22EA', + ntrianglelefteq: '\u22EC', + ntriangleright: '\u22EB', + ntrianglerighteq: '\u22ED', + Nu: '\u039D', + nu: '\u03BD', + num: '\u0023', + numero: '\u2116', + numsp: '\u2007', + nvap: '\u224D\u20D2', + nVDash: '\u22AF', + nVdash: '\u22AE', + nvDash: '\u22AD', + nvdash: '\u22AC', + nvge: '\u2265\u20D2', + nvgt: '\u003E\u20D2', + nvHarr: '\u2904', + nvinfin: '\u29DE', + nvlArr: '\u2902', + nvle: '\u2264\u20D2', + nvlt: '\u003C\u20D2', + nvltrie: '\u22B4\u20D2', + nvrArr: '\u2903', + nvrtrie: '\u22B5\u20D2', + nvsim: '\u223C\u20D2', + nwarhk: '\u2923', + nwArr: '\u21D6', + nwarr: '\u2196', + nwarrow: '\u2196', + nwnear: '\u2927', + Oacute: '\u00D3', + oacute: '\u00F3', + oast: '\u229B', + ocir: '\u229A', + Ocirc: '\u00D4', + ocirc: '\u00F4', + Ocy: '\u041E', + ocy: '\u043E', + odash: '\u229D', + Odblac: '\u0150', + odblac: '\u0151', + odiv: '\u2A38', + odot: '\u2299', + odsold: '\u29BC', + OElig: '\u0152', + oelig: '\u0153', + ofcir: '\u29BF', + Ofr: '\uD835\uDD12', + ofr: '\uD835\uDD2C', + ogon: '\u02DB', + Ograve: '\u00D2', + ograve: '\u00F2', + ogt: '\u29C1', + ohbar: '\u29B5', + ohm: '\u03A9', + oint: '\u222E', + olarr: '\u21BA', + olcir: '\u29BE', + olcross: '\u29BB', + oline: '\u203E', + olt: '\u29C0', + Omacr: '\u014C', + omacr: '\u014D', + Omega: '\u03A9', + omega: '\u03C9', + Omicron: '\u039F', + omicron: '\u03BF', + omid: '\u29B6', + ominus: '\u2296', + Oopf: '\uD835\uDD46', + oopf: '\uD835\uDD60', + opar: '\u29B7', + OpenCurlyDoubleQuote: '\u201C', + OpenCurlyQuote: '\u2018', + operp: '\u29B9', + oplus: '\u2295', + Or: '\u2A54', + or: '\u2228', + orarr: '\u21BB', + ord: '\u2A5D', + order: '\u2134', + orderof: '\u2134', + ordf: '\u00AA', + ordm: '\u00BA', + origof: '\u22B6', + oror: '\u2A56', + orslope: '\u2A57', + orv: '\u2A5B', + oS: '\u24C8', + Oscr: '\uD835\uDCAA', + oscr: '\u2134', + Oslash: '\u00D8', + oslash: '\u00F8', + osol: '\u2298', + Otilde: '\u00D5', + otilde: '\u00F5', + Otimes: '\u2A37', + otimes: '\u2297', + otimesas: '\u2A36', + Ouml: '\u00D6', + ouml: '\u00F6', + ovbar: '\u233D', + OverBar: '\u203E', + OverBrace: '\u23DE', + OverBracket: '\u23B4', + OverParenthesis: '\u23DC', + par: '\u2225', + para: '\u00B6', + parallel: '\u2225', + parsim: '\u2AF3', + parsl: '\u2AFD', + part: '\u2202', + PartialD: '\u2202', + Pcy: '\u041F', + pcy: '\u043F', + percnt: '\u0025', + period: '\u002E', + permil: '\u2030', + perp: '\u22A5', + pertenk: '\u2031', + Pfr: '\uD835\uDD13', + pfr: '\uD835\uDD2D', + Phi: '\u03A6', + phi: '\u03C6', + phiv: '\u03D5', + phmmat: '\u2133', + phone: '\u260E', + Pi: '\u03A0', + pi: '\u03C0', + pitchfork: '\u22D4', + piv: '\u03D6', + planck: '\u210F', + planckh: '\u210E', + plankv: '\u210F', + plus: '\u002B', + plusacir: '\u2A23', + plusb: '\u229E', + pluscir: '\u2A22', + plusdo: '\u2214', + plusdu: '\u2A25', + pluse: '\u2A72', + PlusMinus: '\u00B1', + plusmn: '\u00B1', + plussim: '\u2A26', + plustwo: '\u2A27', + pm: '\u00B1', + Poincareplane: '\u210C', + pointint: '\u2A15', + Popf: '\u2119', + popf: '\uD835\uDD61', + pound: '\u00A3', + Pr: '\u2ABB', + pr: '\u227A', + prap: '\u2AB7', + prcue: '\u227C', + prE: '\u2AB3', + pre: '\u2AAF', + prec: '\u227A', + precapprox: '\u2AB7', + preccurlyeq: '\u227C', + Precedes: '\u227A', + PrecedesEqual: '\u2AAF', + PrecedesSlantEqual: '\u227C', + PrecedesTilde: '\u227E', + preceq: '\u2AAF', + precnapprox: '\u2AB9', + precneqq: '\u2AB5', + precnsim: '\u22E8', + precsim: '\u227E', + Prime: '\u2033', + prime: '\u2032', + primes: '\u2119', + prnap: '\u2AB9', + prnE: '\u2AB5', + prnsim: '\u22E8', + prod: '\u220F', + Product: '\u220F', + profalar: '\u232E', + profline: '\u2312', + profsurf: '\u2313', + prop: '\u221D', + Proportion: '\u2237', + Proportional: '\u221D', + propto: '\u221D', + prsim: '\u227E', + prurel: '\u22B0', + Pscr: '\uD835\uDCAB', + pscr: '\uD835\uDCC5', + Psi: '\u03A8', + psi: '\u03C8', + puncsp: '\u2008', + Qfr: '\uD835\uDD14', + qfr: '\uD835\uDD2E', + qint: '\u2A0C', + Qopf: '\u211A', + qopf: '\uD835\uDD62', + qprime: '\u2057', + Qscr: '\uD835\uDCAC', + qscr: '\uD835\uDCC6', + quaternions: '\u210D', + quatint: '\u2A16', + quest: '\u003F', + questeq: '\u225F', + QUOT: '\u0022', + quot: '\u0022', + rAarr: '\u21DB', + race: '\u223D\u0331', + Racute: '\u0154', + racute: '\u0155', + radic: '\u221A', + raemptyv: '\u29B3', + Rang: '\u27EB', + rang: '\u27E9', + rangd: '\u2992', + range: '\u29A5', + rangle: '\u27E9', + raquo: '\u00BB', + Rarr: '\u21A0', + rArr: '\u21D2', + rarr: '\u2192', + rarrap: '\u2975', + rarrb: '\u21E5', + rarrbfs: '\u2920', + rarrc: '\u2933', + rarrfs: '\u291E', + rarrhk: '\u21AA', + rarrlp: '\u21AC', + rarrpl: '\u2945', + rarrsim: '\u2974', + Rarrtl: '\u2916', + rarrtl: '\u21A3', + rarrw: '\u219D', + rAtail: '\u291C', + ratail: '\u291A', + ratio: '\u2236', + rationals: '\u211A', + RBarr: '\u2910', + rBarr: '\u290F', + rbarr: '\u290D', + rbbrk: '\u2773', + rbrace: '\u007D', + rbrack: '\u005D', + rbrke: '\u298C', + rbrksld: '\u298E', + rbrkslu: '\u2990', + Rcaron: '\u0158', + rcaron: '\u0159', + Rcedil: '\u0156', + rcedil: '\u0157', + rceil: '\u2309', + rcub: '\u007D', + Rcy: '\u0420', + rcy: '\u0440', + rdca: '\u2937', + rdldhar: '\u2969', + rdquo: '\u201D', + rdquor: '\u201D', + rdsh: '\u21B3', + Re: '\u211C', + real: '\u211C', + realine: '\u211B', + realpart: '\u211C', + reals: '\u211D', + rect: '\u25AD', + REG: '\u00AE', + reg: '\u00AE', + ReverseElement: '\u220B', + ReverseEquilibrium: '\u21CB', + ReverseUpEquilibrium: '\u296F', + rfisht: '\u297D', + rfloor: '\u230B', + Rfr: '\u211C', + rfr: '\uD835\uDD2F', + rHar: '\u2964', + rhard: '\u21C1', + rharu: '\u21C0', + rharul: '\u296C', + Rho: '\u03A1', + rho: '\u03C1', + rhov: '\u03F1', + RightAngleBracket: '\u27E9', + RightArrow: '\u2192', + Rightarrow: '\u21D2', + rightarrow: '\u2192', + RightArrowBar: '\u21E5', + RightArrowLeftArrow: '\u21C4', + rightarrowtail: '\u21A3', + RightCeiling: '\u2309', + RightDoubleBracket: '\u27E7', + RightDownTeeVector: '\u295D', + RightDownVector: '\u21C2', + RightDownVectorBar: '\u2955', + RightFloor: '\u230B', + rightharpoondown: '\u21C1', + rightharpoonup: '\u21C0', + rightleftarrows: '\u21C4', + rightleftharpoons: '\u21CC', + rightrightarrows: '\u21C9', + rightsquigarrow: '\u219D', + RightTee: '\u22A2', + RightTeeArrow: '\u21A6', + RightTeeVector: '\u295B', + rightthreetimes: '\u22CC', + RightTriangle: '\u22B3', + RightTriangleBar: '\u29D0', + RightTriangleEqual: '\u22B5', + RightUpDownVector: '\u294F', + RightUpTeeVector: '\u295C', + RightUpVector: '\u21BE', + RightUpVectorBar: '\u2954', + RightVector: '\u21C0', + RightVectorBar: '\u2953', + ring: '\u02DA', + risingdotseq: '\u2253', + rlarr: '\u21C4', + rlhar: '\u21CC', + rlm: '\u200F', + rmoust: '\u23B1', + rmoustache: '\u23B1', + rnmid: '\u2AEE', + roang: '\u27ED', + roarr: '\u21FE', + robrk: '\u27E7', + ropar: '\u2986', + Ropf: '\u211D', + ropf: '\uD835\uDD63', + roplus: '\u2A2E', + rotimes: '\u2A35', + RoundImplies: '\u2970', + rpar: '\u0029', + rpargt: '\u2994', + rppolint: '\u2A12', + rrarr: '\u21C9', + Rrightarrow: '\u21DB', + rsaquo: '\u203A', + Rscr: '\u211B', + rscr: '\uD835\uDCC7', + Rsh: '\u21B1', + rsh: '\u21B1', + rsqb: '\u005D', + rsquo: '\u2019', + rsquor: '\u2019', + rthree: '\u22CC', + rtimes: '\u22CA', + rtri: '\u25B9', + rtrie: '\u22B5', + rtrif: '\u25B8', + rtriltri: '\u29CE', + RuleDelayed: '\u29F4', + ruluhar: '\u2968', + rx: '\u211E', + Sacute: '\u015A', + sacute: '\u015B', + sbquo: '\u201A', + Sc: '\u2ABC', + sc: '\u227B', + scap: '\u2AB8', + Scaron: '\u0160', + scaron: '\u0161', + sccue: '\u227D', + scE: '\u2AB4', + sce: '\u2AB0', + Scedil: '\u015E', + scedil: '\u015F', + Scirc: '\u015C', + scirc: '\u015D', + scnap: '\u2ABA', + scnE: '\u2AB6', + scnsim: '\u22E9', + scpolint: '\u2A13', + scsim: '\u227F', + Scy: '\u0421', + scy: '\u0441', + sdot: '\u22C5', + sdotb: '\u22A1', + sdote: '\u2A66', + searhk: '\u2925', + seArr: '\u21D8', + searr: '\u2198', + searrow: '\u2198', + sect: '\u00A7', + semi: '\u003B', + seswar: '\u2929', + setminus: '\u2216', + setmn: '\u2216', + sext: '\u2736', + Sfr: '\uD835\uDD16', + sfr: '\uD835\uDD30', + sfrown: '\u2322', + sharp: '\u266F', + SHCHcy: '\u0429', + shchcy: '\u0449', + SHcy: '\u0428', + shcy: '\u0448', + ShortDownArrow: '\u2193', + ShortLeftArrow: '\u2190', + shortmid: '\u2223', + shortparallel: '\u2225', + ShortRightArrow: '\u2192', + ShortUpArrow: '\u2191', + shy: '\u00AD', + Sigma: '\u03A3', + sigma: '\u03C3', + sigmaf: '\u03C2', + sigmav: '\u03C2', + sim: '\u223C', + simdot: '\u2A6A', + sime: '\u2243', + simeq: '\u2243', + simg: '\u2A9E', + simgE: '\u2AA0', + siml: '\u2A9D', + simlE: '\u2A9F', + simne: '\u2246', + simplus: '\u2A24', + simrarr: '\u2972', + slarr: '\u2190', + SmallCircle: '\u2218', + smallsetminus: '\u2216', + smashp: '\u2A33', + smeparsl: '\u29E4', + smid: '\u2223', + smile: '\u2323', + smt: '\u2AAA', + smte: '\u2AAC', + smtes: '\u2AAC\uFE00', + SOFTcy: '\u042C', + softcy: '\u044C', + sol: '\u002F', + solb: '\u29C4', + solbar: '\u233F', + Sopf: '\uD835\uDD4A', + sopf: '\uD835\uDD64', + spades: '\u2660', + spadesuit: '\u2660', + spar: '\u2225', + sqcap: '\u2293', + sqcaps: '\u2293\uFE00', + sqcup: '\u2294', + sqcups: '\u2294\uFE00', + Sqrt: '\u221A', + sqsub: '\u228F', + sqsube: '\u2291', + sqsubset: '\u228F', + sqsubseteq: '\u2291', + sqsup: '\u2290', + sqsupe: '\u2292', + sqsupset: '\u2290', + sqsupseteq: '\u2292', + squ: '\u25A1', + Square: '\u25A1', + square: '\u25A1', + SquareIntersection: '\u2293', + SquareSubset: '\u228F', + SquareSubsetEqual: '\u2291', + SquareSuperset: '\u2290', + SquareSupersetEqual: '\u2292', + SquareUnion: '\u2294', + squarf: '\u25AA', + squf: '\u25AA', + srarr: '\u2192', + Sscr: '\uD835\uDCAE', + sscr: '\uD835\uDCC8', + ssetmn: '\u2216', + ssmile: '\u2323', + sstarf: '\u22C6', + Star: '\u22C6', + star: '\u2606', + starf: '\u2605', + straightepsilon: '\u03F5', + straightphi: '\u03D5', + strns: '\u00AF', + Sub: '\u22D0', + sub: '\u2282', + subdot: '\u2ABD', + subE: '\u2AC5', + sube: '\u2286', + subedot: '\u2AC3', + submult: '\u2AC1', + subnE: '\u2ACB', + subne: '\u228A', + subplus: '\u2ABF', + subrarr: '\u2979', + Subset: '\u22D0', + subset: '\u2282', + subseteq: '\u2286', + subseteqq: '\u2AC5', + SubsetEqual: '\u2286', + subsetneq: '\u228A', + subsetneqq: '\u2ACB', + subsim: '\u2AC7', + subsub: '\u2AD5', + subsup: '\u2AD3', + succ: '\u227B', + succapprox: '\u2AB8', + succcurlyeq: '\u227D', + Succeeds: '\u227B', + SucceedsEqual: '\u2AB0', + SucceedsSlantEqual: '\u227D', + SucceedsTilde: '\u227F', + succeq: '\u2AB0', + succnapprox: '\u2ABA', + succneqq: '\u2AB6', + succnsim: '\u22E9', + succsim: '\u227F', + SuchThat: '\u220B', + Sum: '\u2211', + sum: '\u2211', + sung: '\u266A', + Sup: '\u22D1', + sup: '\u2283', + sup1: '\u00B9', + sup2: '\u00B2', + sup3: '\u00B3', + supdot: '\u2ABE', + supdsub: '\u2AD8', + supE: '\u2AC6', + supe: '\u2287', + supedot: '\u2AC4', + Superset: '\u2283', + SupersetEqual: '\u2287', + suphsol: '\u27C9', + suphsub: '\u2AD7', + suplarr: '\u297B', + supmult: '\u2AC2', + supnE: '\u2ACC', + supne: '\u228B', + supplus: '\u2AC0', + Supset: '\u22D1', + supset: '\u2283', + supseteq: '\u2287', + supseteqq: '\u2AC6', + supsetneq: '\u228B', + supsetneqq: '\u2ACC', + supsim: '\u2AC8', + supsub: '\u2AD4', + supsup: '\u2AD6', + swarhk: '\u2926', + swArr: '\u21D9', + swarr: '\u2199', + swarrow: '\u2199', + swnwar: '\u292A', + szlig: '\u00DF', + Tab: '\u0009', + target: '\u2316', + Tau: '\u03A4', + tau: '\u03C4', + tbrk: '\u23B4', + Tcaron: '\u0164', + tcaron: '\u0165', + Tcedil: '\u0162', + tcedil: '\u0163', + Tcy: '\u0422', + tcy: '\u0442', + tdot: '\u20DB', + telrec: '\u2315', + Tfr: '\uD835\uDD17', + tfr: '\uD835\uDD31', + there4: '\u2234', + Therefore: '\u2234', + therefore: '\u2234', + Theta: '\u0398', + theta: '\u03B8', + thetasym: '\u03D1', + thetav: '\u03D1', + thickapprox: '\u2248', + thicksim: '\u223C', + ThickSpace: '\u205F\u200A', + thinsp: '\u2009', + ThinSpace: '\u2009', + thkap: '\u2248', + thksim: '\u223C', + THORN: '\u00DE', + thorn: '\u00FE', + Tilde: '\u223C', + tilde: '\u02DC', + TildeEqual: '\u2243', + TildeFullEqual: '\u2245', + TildeTilde: '\u2248', + times: '\u00D7', + timesb: '\u22A0', + timesbar: '\u2A31', + timesd: '\u2A30', + tint: '\u222D', + toea: '\u2928', + top: '\u22A4', + topbot: '\u2336', + topcir: '\u2AF1', + Topf: '\uD835\uDD4B', + topf: '\uD835\uDD65', + topfork: '\u2ADA', + tosa: '\u2929', + tprime: '\u2034', + TRADE: '\u2122', + trade: '\u2122', + triangle: '\u25B5', + triangledown: '\u25BF', + triangleleft: '\u25C3', + trianglelefteq: '\u22B4', + triangleq: '\u225C', + triangleright: '\u25B9', + trianglerighteq: '\u22B5', + tridot: '\u25EC', + trie: '\u225C', + triminus: '\u2A3A', + TripleDot: '\u20DB', + triplus: '\u2A39', + trisb: '\u29CD', + tritime: '\u2A3B', + trpezium: '\u23E2', + Tscr: '\uD835\uDCAF', + tscr: '\uD835\uDCC9', + TScy: '\u0426', + tscy: '\u0446', + TSHcy: '\u040B', + tshcy: '\u045B', + Tstrok: '\u0166', + tstrok: '\u0167', + twixt: '\u226C', + twoheadleftarrow: '\u219E', + twoheadrightarrow: '\u21A0', + Uacute: '\u00DA', + uacute: '\u00FA', + Uarr: '\u219F', + uArr: '\u21D1', + uarr: '\u2191', + Uarrocir: '\u2949', + Ubrcy: '\u040E', + ubrcy: '\u045E', + Ubreve: '\u016C', + ubreve: '\u016D', + Ucirc: '\u00DB', + ucirc: '\u00FB', + Ucy: '\u0423', + ucy: '\u0443', + udarr: '\u21C5', + Udblac: '\u0170', + udblac: '\u0171', + udhar: '\u296E', + ufisht: '\u297E', + Ufr: '\uD835\uDD18', + ufr: '\uD835\uDD32', + Ugrave: '\u00D9', + ugrave: '\u00F9', + uHar: '\u2963', + uharl: '\u21BF', + uharr: '\u21BE', + uhblk: '\u2580', + ulcorn: '\u231C', + ulcorner: '\u231C', + ulcrop: '\u230F', + ultri: '\u25F8', + Umacr: '\u016A', + umacr: '\u016B', + uml: '\u00A8', + UnderBar: '\u005F', + UnderBrace: '\u23DF', + UnderBracket: '\u23B5', + UnderParenthesis: '\u23DD', + Union: '\u22C3', + UnionPlus: '\u228E', + Uogon: '\u0172', + uogon: '\u0173', + Uopf: '\uD835\uDD4C', + uopf: '\uD835\uDD66', + UpArrow: '\u2191', + Uparrow: '\u21D1', + uparrow: '\u2191', + UpArrowBar: '\u2912', + UpArrowDownArrow: '\u21C5', + UpDownArrow: '\u2195', + Updownarrow: '\u21D5', + updownarrow: '\u2195', + UpEquilibrium: '\u296E', + upharpoonleft: '\u21BF', + upharpoonright: '\u21BE', + uplus: '\u228E', + UpperLeftArrow: '\u2196', + UpperRightArrow: '\u2197', + Upsi: '\u03D2', + upsi: '\u03C5', + upsih: '\u03D2', + Upsilon: '\u03A5', + upsilon: '\u03C5', + UpTee: '\u22A5', + UpTeeArrow: '\u21A5', + upuparrows: '\u21C8', + urcorn: '\u231D', + urcorner: '\u231D', + urcrop: '\u230E', + Uring: '\u016E', + uring: '\u016F', + urtri: '\u25F9', + Uscr: '\uD835\uDCB0', + uscr: '\uD835\uDCCA', + utdot: '\u22F0', + Utilde: '\u0168', + utilde: '\u0169', + utri: '\u25B5', + utrif: '\u25B4', + uuarr: '\u21C8', + Uuml: '\u00DC', + uuml: '\u00FC', + uwangle: '\u29A7', + vangrt: '\u299C', + varepsilon: '\u03F5', + varkappa: '\u03F0', + varnothing: '\u2205', + varphi: '\u03D5', + varpi: '\u03D6', + varpropto: '\u221D', + vArr: '\u21D5', + varr: '\u2195', + varrho: '\u03F1', + varsigma: '\u03C2', + varsubsetneq: '\u228A\uFE00', + varsubsetneqq: '\u2ACB\uFE00', + varsupsetneq: '\u228B\uFE00', + varsupsetneqq: '\u2ACC\uFE00', + vartheta: '\u03D1', + vartriangleleft: '\u22B2', + vartriangleright: '\u22B3', + Vbar: '\u2AEB', + vBar: '\u2AE8', + vBarv: '\u2AE9', + Vcy: '\u0412', + vcy: '\u0432', + VDash: '\u22AB', + Vdash: '\u22A9', + vDash: '\u22A8', + vdash: '\u22A2', + Vdashl: '\u2AE6', + Vee: '\u22C1', + vee: '\u2228', + veebar: '\u22BB', + veeeq: '\u225A', + vellip: '\u22EE', + Verbar: '\u2016', + verbar: '\u007C', + Vert: '\u2016', + vert: '\u007C', + VerticalBar: '\u2223', + VerticalLine: '\u007C', + VerticalSeparator: '\u2758', + VerticalTilde: '\u2240', + VeryThinSpace: '\u200A', + Vfr: '\uD835\uDD19', + vfr: '\uD835\uDD33', + vltri: '\u22B2', + vnsub: '\u2282\u20D2', + vnsup: '\u2283\u20D2', + Vopf: '\uD835\uDD4D', + vopf: '\uD835\uDD67', + vprop: '\u221D', + vrtri: '\u22B3', + Vscr: '\uD835\uDCB1', + vscr: '\uD835\uDCCB', + vsubnE: '\u2ACB\uFE00', + vsubne: '\u228A\uFE00', + vsupnE: '\u2ACC\uFE00', + vsupne: '\u228B\uFE00', + Vvdash: '\u22AA', + vzigzag: '\u299A', + Wcirc: '\u0174', + wcirc: '\u0175', + wedbar: '\u2A5F', + Wedge: '\u22C0', + wedge: '\u2227', + wedgeq: '\u2259', + weierp: '\u2118', + Wfr: '\uD835\uDD1A', + wfr: '\uD835\uDD34', + Wopf: '\uD835\uDD4E', + wopf: '\uD835\uDD68', + wp: '\u2118', + wr: '\u2240', + wreath: '\u2240', + Wscr: '\uD835\uDCB2', + wscr: '\uD835\uDCCC', + xcap: '\u22C2', + xcirc: '\u25EF', + xcup: '\u22C3', + xdtri: '\u25BD', + Xfr: '\uD835\uDD1B', + xfr: '\uD835\uDD35', + xhArr: '\u27FA', + xharr: '\u27F7', + Xi: '\u039E', + xi: '\u03BE', + xlArr: '\u27F8', + xlarr: '\u27F5', + xmap: '\u27FC', + xnis: '\u22FB', + xodot: '\u2A00', + Xopf: '\uD835\uDD4F', + xopf: '\uD835\uDD69', + xoplus: '\u2A01', + xotime: '\u2A02', + xrArr: '\u27F9', + xrarr: '\u27F6', + Xscr: '\uD835\uDCB3', + xscr: '\uD835\uDCCD', + xsqcup: '\u2A06', + xuplus: '\u2A04', + xutri: '\u25B3', + xvee: '\u22C1', + xwedge: '\u22C0', + Yacute: '\u00DD', + yacute: '\u00FD', + YAcy: '\u042F', + yacy: '\u044F', + Ycirc: '\u0176', + ycirc: '\u0177', + Ycy: '\u042B', + ycy: '\u044B', + yen: '\u00A5', + Yfr: '\uD835\uDD1C', + yfr: '\uD835\uDD36', + YIcy: '\u0407', + yicy: '\u0457', + Yopf: '\uD835\uDD50', + yopf: '\uD835\uDD6A', + Yscr: '\uD835\uDCB4', + yscr: '\uD835\uDCCE', + YUcy: '\u042E', + yucy: '\u044E', + Yuml: '\u0178', + yuml: '\u00FF', + Zacute: '\u0179', + zacute: '\u017A', + Zcaron: '\u017D', + zcaron: '\u017E', + Zcy: '\u0417', + zcy: '\u0437', + Zdot: '\u017B', + zdot: '\u017C', + zeetrf: '\u2128', + ZeroWidthSpace: '\u200B', + Zeta: '\u0396', + zeta: '\u03B6', + Zfr: '\u2128', + zfr: '\uD835\uDD37', + ZHcy: '\u0416', + zhcy: '\u0436', + zigrarr: '\u21DD', + Zopf: '\u2124', + zopf: '\uD835\uDD6B', + Zscr: '\uD835\uDCB5', + zscr: '\uD835\uDCCF', + zwj: '\u200D', + zwnj: '\u200C', +}); + +/** + * @deprecated use `HTML_ENTITIES` instead + * @see HTML_ENTITIES + */ +exports.entityMap = exports.HTML_ENTITIES; diff --git a/node_modules/@xmldom/xmldom/lib/index.js b/node_modules/@xmldom/xmldom/lib/index.js new file mode 100644 index 0000000..df827f6 --- /dev/null +++ b/node_modules/@xmldom/xmldom/lib/index.js @@ -0,0 +1,4 @@ +var dom = require('./dom') +exports.DOMImplementation = dom.DOMImplementation +exports.XMLSerializer = dom.XMLSerializer +exports.DOMParser = require('./dom-parser').DOMParser diff --git a/node_modules/@xmldom/xmldom/lib/sax.js b/node_modules/@xmldom/xmldom/lib/sax.js new file mode 100644 index 0000000..2ec3ea1 --- /dev/null +++ b/node_modules/@xmldom/xmldom/lib/sax.js @@ -0,0 +1,662 @@ +var NAMESPACE = require("./conventions").NAMESPACE; + +//[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] +//[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] +//[5] Name ::= NameStartChar (NameChar)* +var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF +var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"); +var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$'); +//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ +//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') + +//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE +//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE +var S_TAG = 0;//tag name offerring +var S_ATTR = 1;//attr name offerring +var S_ATTR_SPACE=2;//attr name end and space offer +var S_EQ = 3;//=space? +var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only) +var S_ATTR_END = 5;//attr value end and no space(quot end) +var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer) +var S_TAG_CLOSE = 7;//closed el + +/** + * Creates an error that will not be caught by XMLReader aka the SAX parser. + * + * @param {string} message + * @param {any?} locator Optional, can provide details about the location in the source + * @constructor + */ +function ParseError(message, locator) { + this.message = message + this.locator = locator + if(Error.captureStackTrace) Error.captureStackTrace(this, ParseError); +} +ParseError.prototype = new Error(); +ParseError.prototype.name = ParseError.name + +function XMLReader(){ + +} + +XMLReader.prototype = { + parse:function(source,defaultNSMap,entityMap){ + var domBuilder = this.domBuilder; + domBuilder.startDocument(); + _copy(defaultNSMap ,defaultNSMap = {}) + parse(source,defaultNSMap,entityMap, + domBuilder,this.errorHandler); + domBuilder.endDocument(); + } +} +function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){ + function fixedFromCharCode(code) { + // String.prototype.fromCharCode does not supports + // > 2 bytes unicode chars directly + if (code > 0xffff) { + code -= 0x10000; + var surrogate1 = 0xd800 + (code >> 10) + , surrogate2 = 0xdc00 + (code & 0x3ff); + + return String.fromCharCode(surrogate1, surrogate2); + } else { + return String.fromCharCode(code); + } + } + function entityReplacer(a){ + var k = a.slice(1,-1); + if (Object.hasOwnProperty.call(entityMap, k)) { + return entityMap[k]; + }else if(k.charAt(0) === '#'){ + return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x'))) + }else{ + errorHandler.error('entity not found:'+a); + return a; + } + } + function appendText(end){//has some bugs + if(end>start){ + var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer); + locator&&position(start); + domBuilder.characters(xt,0,end-start); + start = end + } + } + function position(p,m){ + while(p>=lineEnd && (m = linePattern.exec(source))){ + lineStart = m.index; + lineEnd = lineStart + m[0].length; + locator.lineNumber++; + //console.log('line++:',locator,startPos,endPos) + } + locator.columnNumber = p-lineStart+1; + } + var lineStart = 0; + var lineEnd = 0; + var linePattern = /.*(?:\r\n?|\n)|.*$/g + var locator = domBuilder.locator; + + var parseStack = [{currentNSMap:defaultNSMapCopy}] + var closeMap = {}; + var start = 0; + while(true){ + try{ + var tagStart = source.indexOf('<',start); + if(tagStart<0){ + if(!source.substr(start).match(/^\s*$/)){ + var doc = domBuilder.doc; + var text = doc.createTextNode(source.substr(start)); + doc.appendChild(text); + domBuilder.currentElement = text; + } + return; + } + if(tagStart>start){ + appendText(tagStart); + } + switch(source.charAt(tagStart+1)){ + case '/': + var end = source.indexOf('>',tagStart+3); + var tagName = source.substring(tagStart + 2, end).replace(/[ \t\n\r]+$/g, ''); + var config = parseStack.pop(); + if(end<0){ + + tagName = source.substring(tagStart+2).replace(/[\s<].*/,''); + errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName); + end = tagStart+1+tagName.length; + }else if(tagName.match(/\s + locator&&position(tagStart); + end = parseInstruction(source,tagStart,domBuilder); + break; + case '!':// start){ + start = end; + }else{ + //TODO: 这里有可能sax回退,有位置错误风险 + appendText(Math.max(tagStart,start)+1); + } + } +} +function copyLocator(f,t){ + t.lineNumber = f.lineNumber; + t.columnNumber = f.columnNumber; + return t; +} + +/** + * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack); + * @return end of the elementStartPart(end of elementEndPart for selfClosed el) + */ +function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){ + + /** + * @param {string} qname + * @param {string} value + * @param {number} startIndex + */ + function addAttribute(qname, value, startIndex) { + if (el.attributeNames.hasOwnProperty(qname)) { + errorHandler.fatalError('Attribute ' + qname + ' redefined') + } + el.addValue( + qname, + // @see https://www.w3.org/TR/xml/#AVNormalize + // since the xmldom sax parser does not "interpret" DTD the following is not implemented: + // - recursive replacement of (DTD) entity references + // - trimming and collapsing multiple spaces into a single one for attributes that are not of type CDATA + value.replace(/[\t\n\r]/g, ' ').replace(/&#?\w+;/g, entityReplacer), + startIndex + ) + } + var attrName; + var value; + var p = ++start; + var s = S_TAG;//status + while(true){ + var c = source.charAt(p); + switch(c){ + case '=': + if(s === S_ATTR){//attrName + attrName = source.slice(start,p); + s = S_EQ; + }else if(s === S_ATTR_SPACE){ + s = S_EQ; + }else{ + //fatalError: equal must after attrName or space after attrName + throw new Error('attribute equal must after attrName'); // No known test case + } + break; + case '\'': + case '"': + if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE + ){//equal + if(s === S_ATTR){ + errorHandler.warning('attribute value must after "="') + attrName = source.slice(start,p) + } + start = p+1; + p = source.indexOf(c,start) + if(p>0){ + value = source.slice(start, p); + addAttribute(attrName, value, start-1); + s = S_ATTR_END; + }else{ + //fatalError: no end quot match + throw new Error('attribute value no end \''+c+'\' match'); + } + }else if(s == S_ATTR_NOQUOT_VALUE){ + value = source.slice(start, p); + addAttribute(attrName, value, start); + errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!'); + start = p+1; + s = S_ATTR_END + }else{ + //fatalError: no equal before + throw new Error('attribute value must after "="'); // No known test case + } + break; + case '/': + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p)); + case S_ATTR_END: + case S_TAG_SPACE: + case S_TAG_CLOSE: + s =S_TAG_CLOSE; + el.closed = true; + case S_ATTR_NOQUOT_VALUE: + case S_ATTR: + break; + case S_ATTR_SPACE: + el.closed = true; + break; + //case S_EQ: + default: + throw new Error("attribute invalid close char('/')") // No known test case + } + break; + case ''://end document + errorHandler.error('unexpected end of input'); + if(s == S_TAG){ + el.setTagName(source.slice(start,p)); + } + return p; + case '>': + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p)); + case S_ATTR_END: + case S_TAG_SPACE: + case S_TAG_CLOSE: + break;//normal + case S_ATTR_NOQUOT_VALUE://Compatible state + case S_ATTR: + value = source.slice(start,p); + if(value.slice(-1) === '/'){ + el.closed = true; + value = value.slice(0,-1) + } + case S_ATTR_SPACE: + if(s === S_ATTR_SPACE){ + value = attrName; + } + if(s == S_ATTR_NOQUOT_VALUE){ + errorHandler.warning('attribute "'+value+'" missed quot(")!'); + addAttribute(attrName, value, start) + }else{ + if(!NAMESPACE.isHTML(currentNSMap['']) || !value.match(/^(?:disabled|checked|selected)$/i)){ + errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!') + } + addAttribute(value, value, start) + } + break; + case S_EQ: + throw new Error('attribute value missed!!'); + } +// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) + return p; + /*xml space '\x20' | #x9 | #xD | #xA; */ + case '\u0080': + c = ' '; + default: + if(c<= ' '){//space + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p));//tagName + s = S_TAG_SPACE; + break; + case S_ATTR: + attrName = source.slice(start,p) + s = S_ATTR_SPACE; + break; + case S_ATTR_NOQUOT_VALUE: + var value = source.slice(start, p); + errorHandler.warning('attribute "'+value+'" missed quot(")!!'); + addAttribute(attrName, value, start) + case S_ATTR_END: + s = S_TAG_SPACE; + break; + //case S_TAG_SPACE: + //case S_EQ: + //case S_ATTR_SPACE: + // void();break; + //case S_TAG_CLOSE: + //ignore warning + } + }else{//not space +//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE +//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE + switch(s){ + //case S_TAG:void();break; + //case S_ATTR:void();break; + //case S_ATTR_NOQUOT_VALUE:void();break; + case S_ATTR_SPACE: + var tagName = el.tagName; + if (!NAMESPACE.isHTML(currentNSMap['']) || !attrName.match(/^(?:disabled|checked|selected)$/i)) { + errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!') + } + addAttribute(attrName, attrName, start); + start = p; + s = S_ATTR; + break; + case S_ATTR_END: + errorHandler.warning('attribute space is required"'+attrName+'"!!') + case S_TAG_SPACE: + s = S_ATTR; + start = p; + break; + case S_EQ: + s = S_ATTR_NOQUOT_VALUE; + start = p; + break; + case S_TAG_CLOSE: + throw new Error("elements closed character '/' and '>' must be connected to"); + } + } + }//end outer switch + //console.log('p++',p) + p++; + } +} +/** + * @return true if has new namespace define + */ +function appendElement(el,domBuilder,currentNSMap){ + var tagName = el.tagName; + var localNSMap = null; + //var currentNSMap = parseStack[parseStack.length-1].currentNSMap; + var i = el.length; + while(i--){ + var a = el[i]; + var qName = a.qName; + var value = a.value; + var nsp = qName.indexOf(':'); + if(nsp>0){ + var prefix = a.prefix = qName.slice(0,nsp); + var localName = qName.slice(nsp+1); + var nsPrefix = prefix === 'xmlns' && localName + }else{ + localName = qName; + prefix = null + nsPrefix = qName === 'xmlns' && '' + } + //can not set prefix,because prefix !== '' + a.localName = localName ; + //prefix == null for no ns prefix attribute + if(nsPrefix !== false){//hack!! + if(localNSMap == null){ + localNSMap = {} + //console.log(currentNSMap,0) + _copy(currentNSMap,currentNSMap={}) + //console.log(currentNSMap,1) + } + currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value; + a.uri = NAMESPACE.XMLNS + domBuilder.startPrefixMapping(nsPrefix, value) + } + } + var i = el.length; + while(i--){ + a = el[i]; + var prefix = a.prefix; + if(prefix){//no prefix attribute has no namespace + if(prefix === 'xml'){ + a.uri = NAMESPACE.XML; + }if(prefix !== 'xmlns'){ + a.uri = currentNSMap[prefix || ''] + + //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)} + } + } + } + var nsp = tagName.indexOf(':'); + if(nsp>0){ + prefix = el.prefix = tagName.slice(0,nsp); + localName = el.localName = tagName.slice(nsp+1); + }else{ + prefix = null;//important!! + localName = el.localName = tagName; + } + //no prefix element has default namespace + var ns = el.uri = currentNSMap[prefix || '']; + domBuilder.startElement(ns,localName,tagName,el); + //endPrefixMapping and startPrefixMapping have not any help for dom builder + //localNSMap = null + if(el.closed){ + domBuilder.endElement(ns,localName,tagName); + if(localNSMap){ + for (prefix in localNSMap) { + if (Object.prototype.hasOwnProperty.call(localNSMap, prefix)) { + domBuilder.endPrefixMapping(prefix); + } + } + } + }else{ + el.currentNSMap = currentNSMap; + el.localNSMap = localNSMap; + //parseStack.push(el); + return true; + } +} +function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){ + if(/^(?:script|textarea)$/i.test(tagName)){ + var elEndStart = source.indexOf('',elStartEnd); + var text = source.substring(elStartEnd+1,elEndStart); + if(/[&<]/.test(text)){ + if(/^script$/i.test(tagName)){ + //if(!/\]\]>/.test(text)){ + //lexHandler.startCDATA(); + domBuilder.characters(text,0,text.length); + //lexHandler.endCDATA(); + return elEndStart; + //} + }//}else{//text area + text = text.replace(/&#?\w+;/g,entityReplacer); + domBuilder.characters(text,0,text.length); + return elEndStart; + //} + + } + } + return elStartEnd+1; +} +function fixSelfClosed(source,elStartEnd,tagName,closeMap){ + //if(tagName in closeMap){ + var pos = closeMap[tagName]; + if(pos == null){ + //console.log(tagName) + pos = source.lastIndexOf('') + if(pos',start+4); + //append comment source.substring(4,end)// +![Overview of related specifications and their relations](docs/specs.svg) + +### DOM Parsing and Serialization + +From the [W3C DOM Parsing and Serialization (WD 2016)](https://www.w3.org/TR/2016/WD-DOM-Parsing-20160517/) `xmldom` provides an implementation for the interfaces: +- `DOMParser` +- `XMLSerializer` + +Note that there are some known deviations between this implementation and the W3 specifications. + +Note: [The latest version of this spec](https://w3c.github.io/DOM-Parsing/) has the status "Editors Draft", since it is under active development. One major change is that [the definition of the `DOMParser` interface has been moved to the HTML spec](https://w3c.github.io/DOM-Parsing/#the-domparser-interface) + + +### DOM + +The original author claims that xmldom implements [DOM Level 2] in a "fully compatible" way and some parts of [DOM Level 3], but there are not enough tests to prove this. Both Specifications are now superseded by the [DOM Level 4 aka Living standard] wich has a much broader scope than xmldom. + +xmldom implements the following interfaces (most constructors are currently not exposed): +- `Attr` +- `CDATASection` +- `CharacterData` +- `Comment` +- `Document` +- `DocumentFragment` +- `DocumentType` +- `DOMException` (constructor exposed) +- `DOMImplementation` (constructor exposed) +- `Element` +- `Entity` +- `EntityReference` +- `LiveNodeList` +- `NamedNodeMap` +- `Node` (constructor exposed) +- `NodeList` +- `Notation` +- `ProcessingInstruction` +- `Text` + +more details are available in the (incomplete) [API Reference](#api-reference) section. + +### HTML + +xmldom does not have any goal of supporting the full spec, but it has some capability to parse, report and serialize things differently when "detecting HTML" (by checking the default namespace). +There is an upcoming change to better align the implementation with the latest specs, related to . + +### SAX, XML, XMLNS + +xmldom has an own SAX parser implementation to do the actual parsing, which implements some interfaces in alignment with the Java interfaces SAX defines: +- `XMLReader` +- `DOMHandler` + +There is an idea/proposal to make it possible to replace it with something else in diff --git a/node_modules/ansi/.jshintrc b/node_modules/ansi/.jshintrc new file mode 100644 index 0000000..248c542 --- /dev/null +++ b/node_modules/ansi/.jshintrc @@ -0,0 +1,4 @@ +{ + "laxcomma": true, + "asi": true +} diff --git a/node_modules/ansi/.npmignore b/node_modules/ansi/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/node_modules/ansi/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/node_modules/ansi/History.md b/node_modules/ansi/History.md new file mode 100644 index 0000000..aea8aaf --- /dev/null +++ b/node_modules/ansi/History.md @@ -0,0 +1,23 @@ + +0.3.1 / 2016-01-14 +================== + + * add MIT LICENSE file (#23, @kasicka) + * preserve chaining after redundant style-method calls (#19, @drewblaisdell) + * package: add "license" field (#16, @BenjaminTsai) + +0.3.0 / 2014-05-09 +================== + + * package: remove "test" script and "devDependencies" + * package: remove "engines" section + * pacakge: remove "bin" section + * package: beautify + * examples: remove `starwars` example (#15) + * Documented goto, horizontalAbsolute, and eraseLine methods in README.md (#12, @Jammerwoch) + * add `.jshintrc` file + +< 0.3.0 +======= + + * Prehistoric diff --git a/node_modules/ansi/LICENSE b/node_modules/ansi/LICENSE new file mode 100644 index 0000000..2ea4dc5 --- /dev/null +++ b/node_modules/ansi/LICENSE @@ -0,0 +1,24 @@ +(The MIT License) + +Copyright (c) 2012 Nathan Rajlich + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ansi/README.md b/node_modules/ansi/README.md new file mode 100644 index 0000000..6ce1940 --- /dev/null +++ b/node_modules/ansi/README.md @@ -0,0 +1,98 @@ +ansi.js +========= +### Advanced ANSI formatting tool for Node.js + +`ansi.js` is a module for Node.js that provides an easy-to-use API for +writing ANSI escape codes to `Stream` instances. ANSI escape codes are used to do +fancy things in a terminal window, like render text in colors, delete characters, +lines, the entire window, or hide and show the cursor, and lots more! + +#### Features: + + * 256 color support for the terminal! + * Make a beep sound from your terminal! + * Works with *any* writable `Stream` instance. + * Allows you to move the cursor anywhere on the terminal window. + * Allows you to delete existing contents from the terminal window. + * Allows you to hide and show the cursor. + * Converts CSS color codes and RGB values into ANSI escape codes. + * Low-level; you are in control of when escape codes are used, it's not abstracted. + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install ansi +``` + + +Example +------- + +``` js +var ansi = require('ansi') + , cursor = ansi(process.stdout) + +// You can chain your calls forever: +cursor + .red() // Set font color to red + .bg.grey() // Set background color to grey + .write('Hello World!') // Write 'Hello World!' to stdout + .bg.reset() // Reset the bgcolor before writing the trailing \n, + // to avoid Terminal glitches + .write('\n') // And a final \n to wrap things up + +// Rendering modes are persistent: +cursor.hex('#660000').bold().underline() + +// You can use the regular logging functions, text will be green: +console.log('This is blood red, bold text') + +// To reset just the foreground color: +cursor.fg.reset() + +console.log('This will still be bold') + +// to go to a location (x,y) on the console +// note: 1-indexed, not 0-indexed: +cursor.goto(10, 5).write('Five down, ten over') + +// to clear the current line: +cursor.horizontalAbsolute(0).eraseLine().write('Starting again') + +// to go to a different column on the current line: +cursor.horizontalAbsolute(5).write('column five') + +// Clean up after yourself! +cursor.reset() +``` + + +License +------- + +(The MIT License) + +Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/ansi/examples/beep/index.js b/node_modules/ansi/examples/beep/index.js new file mode 100644 index 0000000..c1ec929 --- /dev/null +++ b/node_modules/ansi/examples/beep/index.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +/** + * Invokes the terminal "beep" sound once per second on every exact second. + */ + +process.title = 'beep' + +var cursor = require('../../')(process.stdout) + +function beep () { + cursor.beep() + setTimeout(beep, 1000 - (new Date()).getMilliseconds()) +} + +setTimeout(beep, 1000 - (new Date()).getMilliseconds()) diff --git a/node_modules/ansi/examples/clear/index.js b/node_modules/ansi/examples/clear/index.js new file mode 100644 index 0000000..6ac21ff --- /dev/null +++ b/node_modules/ansi/examples/clear/index.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +/** + * Like GNU ncurses "clear" command. + * https://github.com/mscdex/node-ncurses/blob/master/deps/ncurses/progs/clear.c + */ + +process.title = 'clear' + +function lf () { return '\n' } + +require('../../')(process.stdout) + .write(Array.apply(null, Array(process.stdout.getWindowSize()[1])).map(lf).join('')) + .eraseData(2) + .goto(1, 1) diff --git a/node_modules/ansi/examples/cursorPosition.js b/node_modules/ansi/examples/cursorPosition.js new file mode 100644 index 0000000..50f9644 --- /dev/null +++ b/node_modules/ansi/examples/cursorPosition.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +var tty = require('tty') +var cursor = require('../')(process.stdout) + +// listen for the queryPosition report on stdin +process.stdin.resume() +raw(true) + +process.stdin.once('data', function (b) { + var match = /\[(\d+)\;(\d+)R$/.exec(b.toString()) + if (match) { + var xy = match.slice(1, 3).reverse().map(Number) + console.error(xy) + } + + // cleanup and close stdin + raw(false) + process.stdin.pause() +}) + + +// send the query position request code to stdout +cursor.queryPosition() + +function raw (mode) { + if (process.stdin.setRawMode) { + process.stdin.setRawMode(mode) + } else { + tty.setRawMode(mode) + } +} diff --git a/node_modules/ansi/examples/progress/index.js b/node_modules/ansi/examples/progress/index.js new file mode 100644 index 0000000..d28dbda --- /dev/null +++ b/node_modules/ansi/examples/progress/index.js @@ -0,0 +1,87 @@ +#!/usr/bin/env node + +var assert = require('assert') + , ansi = require('../../') + +function Progress (stream, width) { + this.cursor = ansi(stream) + this.delta = this.cursor.newlines + this.width = width | 0 || 10 + this.open = '[' + this.close = ']' + this.complete = '█' + this.incomplete = '_' + + // initial render + this.progress = 0 +} + +Object.defineProperty(Progress.prototype, 'progress', { + get: get + , set: set + , configurable: true + , enumerable: true +}) + +function get () { + return this._progress +} + +function set (v) { + this._progress = Math.max(0, Math.min(v, 100)) + + var w = this.width - this.complete.length - this.incomplete.length + , n = w * (this._progress / 100) | 0 + , i = w - n + , com = c(this.complete, n) + , inc = c(this.incomplete, i) + , delta = this.cursor.newlines - this.delta + + assert.equal(com.length + inc.length, w) + + if (delta > 0) { + this.cursor.up(delta) + this.delta = this.cursor.newlines + } + + this.cursor + .horizontalAbsolute(0) + .eraseLine(2) + .fg.white() + .write(this.open) + .fg.grey() + .bold() + .write(com) + .resetBold() + .write(inc) + .fg.white() + .write(this.close) + .fg.reset() + .write('\n') +} + +function c (char, length) { + return Array.apply(null, Array(length)).map(function () { + return char + }).join('') +} + + + + +// Usage +var width = parseInt(process.argv[2], 10) || process.stdout.getWindowSize()[0] / 2 + , p = new Progress(process.stdout, width) + +;(function tick () { + p.progress += Math.random() * 5 + p.cursor + .eraseLine(2) + .write('Progress: ') + .bold().write(p.progress.toFixed(2)) + .write('%') + .resetBold() + .write('\n') + if (p.progress < 100) + setTimeout(tick, 100) +})() diff --git a/node_modules/ansi/lib/ansi.js b/node_modules/ansi/lib/ansi.js new file mode 100644 index 0000000..b1714e3 --- /dev/null +++ b/node_modules/ansi/lib/ansi.js @@ -0,0 +1,405 @@ + +/** + * References: + * + * - http://en.wikipedia.org/wiki/ANSI_escape_code + * - http://www.termsys.demon.co.uk/vtansi.htm + * + */ + +/** + * Module dependencies. + */ + +var emitNewlineEvents = require('./newlines') + , prefix = '\x1b[' // For all escape codes + , suffix = 'm' // Only for color codes + +/** + * The ANSI escape sequences. + */ + +var codes = { + up: 'A' + , down: 'B' + , forward: 'C' + , back: 'D' + , nextLine: 'E' + , previousLine: 'F' + , horizontalAbsolute: 'G' + , eraseData: 'J' + , eraseLine: 'K' + , scrollUp: 'S' + , scrollDown: 'T' + , savePosition: 's' + , restorePosition: 'u' + , queryPosition: '6n' + , hide: '?25l' + , show: '?25h' +} + +/** + * Rendering ANSI codes. + */ + +var styles = { + bold: 1 + , italic: 3 + , underline: 4 + , inverse: 7 +} + +/** + * The negating ANSI code for the rendering modes. + */ + +var reset = { + bold: 22 + , italic: 23 + , underline: 24 + , inverse: 27 +} + +/** + * The standard, styleable ANSI colors. + */ + +var colors = { + white: 37 + , black: 30 + , blue: 34 + , cyan: 36 + , green: 32 + , magenta: 35 + , red: 31 + , yellow: 33 + , grey: 90 + , brightBlack: 90 + , brightRed: 91 + , brightGreen: 92 + , brightYellow: 93 + , brightBlue: 94 + , brightMagenta: 95 + , brightCyan: 96 + , brightWhite: 97 +} + + +/** + * Creates a Cursor instance based off the given `writable stream` instance. + */ + +function ansi (stream, options) { + if (stream._ansicursor) { + return stream._ansicursor + } else { + return stream._ansicursor = new Cursor(stream, options) + } +} +module.exports = exports = ansi + +/** + * The `Cursor` class. + */ + +function Cursor (stream, options) { + if (!(this instanceof Cursor)) { + return new Cursor(stream, options) + } + if (typeof stream != 'object' || typeof stream.write != 'function') { + throw new Error('a valid Stream instance must be passed in') + } + + // the stream to use + this.stream = stream + + // when 'enabled' is false then all the functions are no-ops except for write() + this.enabled = options && options.enabled + if (typeof this.enabled === 'undefined') { + this.enabled = stream.isTTY + } + this.enabled = !!this.enabled + + // then `buffering` is true, then `write()` calls are buffered in + // memory until `flush()` is invoked + this.buffering = !!(options && options.buffering) + this._buffer = [] + + // controls the foreground and background colors + this.fg = this.foreground = new Colorer(this, 0) + this.bg = this.background = new Colorer(this, 10) + + // defaults + this.Bold = false + this.Italic = false + this.Underline = false + this.Inverse = false + + // keep track of the number of "newlines" that get encountered + this.newlines = 0 + emitNewlineEvents(stream) + stream.on('newline', function () { + this.newlines++ + }.bind(this)) +} +exports.Cursor = Cursor + +/** + * Helper function that calls `write()` on the underlying Stream. + * Returns `this` instead of the write() return value to keep + * the chaining going. + */ + +Cursor.prototype.write = function (data) { + if (this.buffering) { + this._buffer.push(arguments) + } else { + this.stream.write.apply(this.stream, arguments) + } + return this +} + +/** + * Buffer `write()` calls into memory. + * + * @api public + */ + +Cursor.prototype.buffer = function () { + this.buffering = true + return this +} + +/** + * Write out the in-memory buffer. + * + * @api public + */ + +Cursor.prototype.flush = function () { + this.buffering = false + var str = this._buffer.map(function (args) { + if (args.length != 1) throw new Error('unexpected args length! ' + args.length); + return args[0]; + }).join(''); + this._buffer.splice(0); // empty + this.write(str); + return this +} + + +/** + * The `Colorer` class manages both the background and foreground colors. + */ + +function Colorer (cursor, base) { + this.current = null + this.cursor = cursor + this.base = base +} +exports.Colorer = Colorer + +/** + * Write an ANSI color code, ensuring that the same code doesn't get rewritten. + */ + +Colorer.prototype._setColorCode = function setColorCode (code) { + var c = String(code) + if (this.current === c) return + this.cursor.enabled && this.cursor.write(prefix + c + suffix) + this.current = c + return this +} + + +/** + * Set up the positional ANSI codes. + */ + +Object.keys(codes).forEach(function (name) { + var code = String(codes[name]) + Cursor.prototype[name] = function () { + var c = code + if (arguments.length > 0) { + c = toArray(arguments).map(Math.round).join(';') + code + } + this.enabled && this.write(prefix + c) + return this + } +}) + +/** + * Set up the functions for the rendering ANSI codes. + */ + +Object.keys(styles).forEach(function (style) { + var name = style[0].toUpperCase() + style.substring(1) + , c = styles[style] + , r = reset[style] + + Cursor.prototype[style] = function () { + if (this[name]) return this + this.enabled && this.write(prefix + c + suffix) + this[name] = true + return this + } + + Cursor.prototype['reset' + name] = function () { + if (!this[name]) return this + this.enabled && this.write(prefix + r + suffix) + this[name] = false + return this + } +}) + +/** + * Setup the functions for the standard colors. + */ + +Object.keys(colors).forEach(function (color) { + var code = colors[color] + + Colorer.prototype[color] = function () { + this._setColorCode(this.base + code) + return this.cursor + } + + Cursor.prototype[color] = function () { + return this.foreground[color]() + } +}) + +/** + * Makes a beep sound! + */ + +Cursor.prototype.beep = function () { + this.enabled && this.write('\x07') + return this +} + +/** + * Moves cursor to specific position + */ + +Cursor.prototype.goto = function (x, y) { + x = x | 0 + y = y | 0 + this.enabled && this.write(prefix + y + ';' + x + 'H') + return this +} + +/** + * Resets the color. + */ + +Colorer.prototype.reset = function () { + this._setColorCode(this.base + 39) + return this.cursor +} + +/** + * Resets all ANSI formatting on the stream. + */ + +Cursor.prototype.reset = function () { + this.enabled && this.write(prefix + '0' + suffix) + this.Bold = false + this.Italic = false + this.Underline = false + this.Inverse = false + this.foreground.current = null + this.background.current = null + return this +} + +/** + * Sets the foreground color with the given RGB values. + * The closest match out of the 216 colors is picked. + */ + +Colorer.prototype.rgb = function (r, g, b) { + var base = this.base + 38 + , code = rgb(r, g, b) + this._setColorCode(base + ';5;' + code) + return this.cursor +} + +/** + * Same as `cursor.fg.rgb(r, g, b)`. + */ + +Cursor.prototype.rgb = function (r, g, b) { + return this.foreground.rgb(r, g, b) +} + +/** + * Accepts CSS color codes for use with ANSI escape codes. + * For example: `#FF000` would be bright red. + */ + +Colorer.prototype.hex = function (color) { + return this.rgb.apply(this, hex(color)) +} + +/** + * Same as `cursor.fg.hex(color)`. + */ + +Cursor.prototype.hex = function (color) { + return this.foreground.hex(color) +} + + +// UTIL FUNCTIONS // + +/** + * Translates a 255 RGB value to a 0-5 ANSI RGV value, + * then returns the single ANSI color code to use. + */ + +function rgb (r, g, b) { + var red = r / 255 * 5 + , green = g / 255 * 5 + , blue = b / 255 * 5 + return rgb5(red, green, blue) +} + +/** + * Turns rgb 0-5 values into a single ANSI color code to use. + */ + +function rgb5 (r, g, b) { + var red = Math.round(r) + , green = Math.round(g) + , blue = Math.round(b) + return 16 + (red*36) + (green*6) + blue +} + +/** + * Accepts a hex CSS color code string (# is optional) and + * translates it into an Array of 3 RGB 0-255 values, which + * can then be used with rgb(). + */ + +function hex (color) { + var c = color[0] === '#' ? color.substring(1) : color + , r = c.substring(0, 2) + , g = c.substring(2, 4) + , b = c.substring(4, 6) + return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)] +} + +/** + * Turns an array-like object into a real array. + */ + +function toArray (a) { + var i = 0 + , l = a.length + , rtn = [] + for (; i 0) { + var len = data.length + , i = 0 + // now try to calculate any deltas + if (typeof data == 'string') { + for (; i (http://tootallnate.net)", + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/ansi.js.git" + }, + "main": "./lib/ansi.js" +} diff --git a/node_modules/balanced-match/.github/FUNDING.yml b/node_modules/balanced-match/.github/FUNDING.yml new file mode 100644 index 0000000..cea8b16 --- /dev/null +++ b/node_modules/balanced-match/.github/FUNDING.yml @@ -0,0 +1,2 @@ +tidelift: "npm/balanced-match" +patreon: juliangruber diff --git a/node_modules/balanced-match/LICENSE.md b/node_modules/balanced-match/LICENSE.md new file mode 100644 index 0000000..2cdc8e4 --- /dev/null +++ b/node_modules/balanced-match/LICENSE.md @@ -0,0 +1,21 @@ +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/balanced-match/README.md b/node_modules/balanced-match/README.md new file mode 100644 index 0000000..d2a48b6 --- /dev/null +++ b/node_modules/balanced-match/README.md @@ -0,0 +1,97 @@ +# balanced-match + +Match balanced string pairs, like `{` and `}` or `` and ``. Supports regular expressions as well! + +[![build status](https://secure.travis-ci.org/juliangruber/balanced-match.svg)](http://travis-ci.org/juliangruber/balanced-match) +[![downloads](https://img.shields.io/npm/dm/balanced-match.svg)](https://www.npmjs.org/package/balanced-match) + +[![testling badge](https://ci.testling.com/juliangruber/balanced-match.png)](https://ci.testling.com/juliangruber/balanced-match) + +## Example + +Get the first matching pair of braces: + +```js +var balanced = require('balanced-match'); + +console.log(balanced('{', '}', 'pre{in{nested}}post')); +console.log(balanced('{', '}', 'pre{first}between{second}post')); +console.log(balanced(/\s+\{\s+/, /\s+\}\s+/, 'pre { in{nest} } post')); +``` + +The matches are: + +```bash +$ node example.js +{ start: 3, end: 14, pre: 'pre', body: 'in{nested}', post: 'post' } +{ start: 3, + end: 9, + pre: 'pre', + body: 'first', + post: 'between{second}post' } +{ start: 3, end: 17, pre: 'pre', body: 'in{nest}', post: 'post' } +``` + +## API + +### var m = balanced(a, b, str) + +For the first non-nested matching pair of `a` and `b` in `str`, return an +object with those keys: + +* **start** the index of the first match of `a` +* **end** the index of the matching `b` +* **pre** the preamble, `a` and `b` not included +* **body** the match, `a` and `b` not included +* **post** the postscript, `a` and `b` not included + +If there's no match, `undefined` will be returned. + +If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `['{', 'a', '']` and `{a}}` will match `['', 'a', '}']`. + +### var r = balanced.range(a, b, str) + +For the first non-nested matching pair of `a` and `b` in `str`, return an +array with indexes: `[ , ]`. + +If there's no match, `undefined` will be returned. + +If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `[ 1, 3 ]` and `{a}}` will match `[0, 2]`. + +## Installation + +With [npm](https://npmjs.org) do: + +```bash +npm install balanced-match +``` + +## Security contact information + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. + +## License + +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/balanced-match/index.js b/node_modules/balanced-match/index.js new file mode 100644 index 0000000..c67a646 --- /dev/null +++ b/node_modules/balanced-match/index.js @@ -0,0 +1,62 @@ +'use strict'; +module.exports = balanced; +function balanced(a, b, str) { + if (a instanceof RegExp) a = maybeMatch(a, str); + if (b instanceof RegExp) b = maybeMatch(b, str); + + var r = range(a, b, str); + + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} + +function maybeMatch(reg, str) { + var m = str.match(reg); + return m ? m[0] : null; +} + +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; + + if (ai >= 0 && bi > 0) { + if(a===b) { + return [ai, bi]; + } + begs = []; + left = str.length; + + while (i >= 0 && !result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; + } + + bi = str.indexOf(b, i + 1); + } + + i = ai < bi && ai >= 0 ? ai : bi; + } + + if (begs.length) { + result = [ left, right ]; + } + } + + return result; +} diff --git a/node_modules/balanced-match/package.json b/node_modules/balanced-match/package.json new file mode 100644 index 0000000..ce6073e --- /dev/null +++ b/node_modules/balanced-match/package.json @@ -0,0 +1,48 @@ +{ + "name": "balanced-match", + "description": "Match balanced character pairs, like \"{\" and \"}\"", + "version": "1.0.2", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/balanced-match.git" + }, + "homepage": "https://github.com/juliangruber/balanced-match", + "main": "index.js", + "scripts": { + "test": "tape test/test.js", + "bench": "matcha test/bench.js" + }, + "devDependencies": { + "matcha": "^0.7.0", + "tape": "^4.6.0" + }, + "keywords": [ + "match", + "regexp", + "test", + "balanced", + "parse" + ], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT", + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/8..latest", + "firefox/20..latest", + "firefox/nightly", + "chrome/25..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + } +} diff --git a/node_modules/base64-js/LICENSE b/node_modules/base64-js/LICENSE new file mode 100644 index 0000000..6d52b8a --- /dev/null +++ b/node_modules/base64-js/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Jameson Little + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/base64-js/README.md b/node_modules/base64-js/README.md new file mode 100644 index 0000000..b42a48f --- /dev/null +++ b/node_modules/base64-js/README.md @@ -0,0 +1,34 @@ +base64-js +========= + +`base64-js` does basic base64 encoding/decoding in pure JS. + +[![build status](https://secure.travis-ci.org/beatgammit/base64-js.png)](http://travis-ci.org/beatgammit/base64-js) + +Many browsers already have base64 encoding/decoding functionality, but it is for text data, not all-purpose binary data. + +Sometimes encoding/decoding binary data in the browser is useful, and that is what this module does. + +## install + +With [npm](https://npmjs.org) do: + +`npm install base64-js` and `var base64js = require('base64-js')` + +For use in web browsers do: + +`` + +[Get supported base64-js with the Tidelift Subscription](https://tidelift.com/subscription/pkg/npm-base64-js?utm_source=npm-base64-js&utm_medium=referral&utm_campaign=readme) + +## methods + +`base64js` has three exposed functions, `byteLength`, `toByteArray` and `fromByteArray`, which both take a single argument. + +* `byteLength` - Takes a base64 string and returns length of byte array +* `toByteArray` - Takes a base64 string and returns a byte array +* `fromByteArray` - Takes a byte array and returns a base64 string + +## license + +MIT diff --git a/node_modules/base64-js/base64js.min.js b/node_modules/base64-js/base64js.min.js new file mode 100644 index 0000000..908ac83 --- /dev/null +++ b/node_modules/base64-js/base64js.min.js @@ -0,0 +1 @@ +(function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"==typeof window?"undefined"==typeof global?"undefined"==typeof self?this:self:global:window,b.base64js=a()}})(function(){return function(){function b(d,e,g){function a(j,i){if(!e[j]){if(!d[j]){var f="function"==typeof require&&require;if(!i&&f)return f(j,!0);if(h)return h(j,!0);var c=new Error("Cannot find module '"+j+"'");throw c.code="MODULE_NOT_FOUND",c}var k=e[j]={exports:{}};d[j][0].call(k.exports,function(b){var c=d[j][1][b];return a(c||b)},k,k.exports,b,d,e,g)}return e[j].exports}for(var h="function"==typeof require&&require,c=0;c>16,j[k++]=255&b>>8,j[k++]=255&b;return 2===h&&(b=l[a.charCodeAt(c)]<<2|l[a.charCodeAt(c+1)]>>4,j[k++]=255&b),1===h&&(b=l[a.charCodeAt(c)]<<10|l[a.charCodeAt(c+1)]<<4|l[a.charCodeAt(c+2)]>>2,j[k++]=255&b>>8,j[k++]=255&b),j}function g(a){return k[63&a>>18]+k[63&a>>12]+k[63&a>>6]+k[63&a]}function h(a,b,c){for(var d,e=[],f=b;fj?j:g+f));return 1===d?(b=a[c-1],e.push(k[b>>2]+k[63&b<<4]+"==")):2===d&&(b=(a[c-2]<<8)+a[c-1],e.push(k[b>>10]+k[63&b>>4]+k[63&b<<2]+"=")),e.join("")}c.byteLength=function(a){var b=d(a),c=b[0],e=b[1];return 3*(c+e)/4-e},c.toByteArray=f,c.fromByteArray=j;for(var k=[],l=[],m="undefined"==typeof Uint8Array?Array:Uint8Array,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,p=n.length;o 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + var validLen = b64.indexOf('=') + if (validLen === -1) validLen = len + + var placeHoldersLen = validLen === len + ? 0 + : 4 - (validLen % 4) + + return [validLen, placeHoldersLen] +} + +// base64 is 4/3 + up to two characters of the original data +function byteLength (b64) { + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function _byteLength (b64, validLen, placeHoldersLen) { + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function toByteArray (b64) { + var tmp + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) + + var curByte = 0 + + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 + ? validLen - 4 + : validLen + + var i + for (i = 0; i < len; i += 4) { + tmp = + (revLookup[b64.charCodeAt(i)] << 18) | + (revLookup[b64.charCodeAt(i + 1)] << 12) | + (revLookup[b64.charCodeAt(i + 2)] << 6) | + revLookup[b64.charCodeAt(i + 3)] + arr[curByte++] = (tmp >> 16) & 0xFF + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 2) { + tmp = + (revLookup[b64.charCodeAt(i)] << 2) | + (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 1) { + tmp = + (revLookup[b64.charCodeAt(i)] << 10) | + (revLookup[b64.charCodeAt(i + 1)] << 4) | + (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + + lookup[num >> 12 & 0x3F] + + lookup[num >> 6 & 0x3F] + + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + parts.push( + lookup[tmp >> 2] + + lookup[(tmp << 4) & 0x3F] + + '==' + ) + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1] + parts.push( + lookup[tmp >> 10] + + lookup[(tmp >> 4) & 0x3F] + + lookup[(tmp << 2) & 0x3F] + + '=' + ) + } + + return parts.join('') +} diff --git a/node_modules/base64-js/package.json b/node_modules/base64-js/package.json new file mode 100644 index 0000000..c3972e3 --- /dev/null +++ b/node_modules/base64-js/package.json @@ -0,0 +1,47 @@ +{ + "name": "base64-js", + "description": "Base64 encoding/decoding in pure JS", + "version": "1.5.1", + "author": "T. Jameson Little ", + "typings": "index.d.ts", + "bugs": { + "url": "https://github.com/beatgammit/base64-js/issues" + }, + "devDependencies": { + "babel-minify": "^0.5.1", + "benchmark": "^2.1.4", + "browserify": "^16.3.0", + "standard": "*", + "tape": "4.x" + }, + "homepage": "https://github.com/beatgammit/base64-js", + "keywords": [ + "base64" + ], + "license": "MIT", + "main": "index.js", + "repository": { + "type": "git", + "url": "git://github.com/beatgammit/base64-js.git" + }, + "scripts": { + "build": "browserify -s base64js -r ./ | minify > base64js.min.js", + "lint": "standard", + "test": "npm run lint && npm run unit", + "unit": "tape test/*.js" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] +} diff --git a/node_modules/big-integer/BigInteger.d.ts b/node_modules/big-integer/BigInteger.d.ts new file mode 100644 index 0000000..168ba5f --- /dev/null +++ b/node_modules/big-integer/BigInteger.d.ts @@ -0,0 +1,2393 @@ +/** + * Type definitions for BigInteger.js + * Definitions by: Tommy Frazier + */ +export = bigInt; +export as namespace bigInt; + +declare var bigInt: bigInt.BigIntegerStatic; + +declare namespace bigInt { + type BigNumber = number | bigint | string | BigInteger; + + interface BigIntegerStatic { + /** + * Equivalent to bigInt(0). + */ + (): BigInteger; + + /** + * Parse a Javascript number into a bigInt. + */ + (number: number): BigInteger; + + /** + * Parse a Javascript native bigint into a bigInt. + */ + (number: bigint): BigInteger; + + /** + * Parse a string into a bigInt. + * Default base is 10. + * Default alphabet is "0123456789abcdefghijklmnopqrstuvwxyz". + * caseSensitive defaults to false. + */ + (string: string, base?: BigNumber, alphabet?: string, caseSensitive?: boolean): BigInteger; + + /** + * no-op. + */ + (bigInt: BigInteger): BigInteger; + + /** + * Constructs a bigInt from an array of digits in specified base. + * The optional isNegative flag will make the number negative. + */ + fromArray: (digits: BigNumber[], base?: BigNumber, isNegative?: boolean) => BigInteger; + + /** + * Finds the greatest common denominator of a and b. + */ + gcd: (a: BigNumber, b: BigNumber) => BigInteger; + + + /** + * Returns true if x is a BigInteger, false otherwise. + */ + isInstance: (x: any) => x is BigInteger; + + /** + * Finds the least common multiple of a and b. + */ + lcm: (a: BigNumber, b: BigNumber) => BigInteger; + + /** + * Returns the largest of a and b. + */ + max: (a: BigNumber, b: BigNumber) => BigInteger; + + /** + * Returns the smallest of a and b. + */ + min: (a: BigNumber, b: BigNumber) => BigInteger; + + /** + * Equivalent to bigInt(-1). + */ + minusOne: BigInteger; + + /** + * Equivalent to bigInt(1). + */ + one: BigInteger; + + /** + * Returns a random number between min and max. + */ + randBetween: (min: BigNumber, max: BigNumber, rng?: () => number) => BigInteger; + + /** + * Equivalent to bigInt(0). + */ + zero: BigInteger; + } + + interface BigInteger { + /** + * Returns the absolute value of a bigInt. + */ + abs(): BigInteger; + + /** + * Performs addition. + */ + add(number: BigNumber): BigInteger; + + /** + * Performs the bitwise AND operation. + */ + and(number: BigNumber): BigInteger; + + /** + * Returns the number of digits required to represent a bigInt in binary. + */ + bitLength(): BigInteger; + + /** + * Performs a comparison between two numbers. If the numbers are equal, it returns 0. + * If the first number is greater, it returns 1. If the first number is lesser, it returns -1. + */ + compare(number: BigNumber): number; + + /** + * Performs a comparison between the absolute value of two numbers. + */ + compareAbs(number: BigNumber): number; + + /** + * Alias for the compare method. + */ + compareTo(number: BigNumber): number; + + /** + * Performs integer division, disregarding the remainder. + */ + divide(number: BigNumber): BigInteger; + + /** + * Performs division and returns an object with two properties: quotient and remainder. + * The sign of the remainder will match the sign of the dividend. + */ + divmod(number: BigNumber): { quotient: BigInteger, remainder: BigInteger }; + + /** + * Alias for the equals method. + */ + eq(number: BigNumber): boolean; + + /** + * Checks if two numbers are equal. + */ + equals(number: BigNumber): boolean; + + /** + * Alias for the greaterOrEquals method. + */ + geq(number: BigNumber): boolean; + + /** + * Checks if the first number is greater than the second. + */ + greater(number: BigNumber): boolean; + + /** + * Checks if the first number is greater than or equal to the second. + */ + greaterOrEquals(number: BigNumber): boolean; + + /** + * Alias for the greater method. + */ + gt(number: BigNumber): boolean; + + /** + * Returns true if the first number is divisible by the second number, false otherwise. + */ + isDivisibleBy(number: BigNumber): boolean; + + /** + * Returns true if the number is even, false otherwise. + */ + isEven(): boolean; + + /** + * Returns true if the number is negative, false otherwise. + * Returns false for 0 and true for -0. + */ + isNegative(): boolean; + + /** + * Returns true if the number is odd, false otherwise. + */ + isOdd(): boolean; + + /** + * Return true if the number is positive, false otherwise. + * Returns true for 0 and false for -0. + */ + isPositive(): boolean; + + /** + * Returns true if the number is prime, false otherwise. + */ + isPrime(strict?: boolean): boolean; + + /** + * Returns true if the number is very likely to be prime, false otherwise. + */ + isProbablePrime(iterations?: number, rng?: () => number): boolean; + + /** + * Returns true if the number is 1 or -1, false otherwise. + */ + isUnit(): boolean; + + /** + * Return true if the number is 0 or -0, false otherwise. + */ + isZero(): boolean; + + /** + * Alias for the lesserOrEquals method. + */ + leq(number: BigNumber): boolean; + + /** + * Checks if the first number is lesser than the second. + */ + lesser(number: BigNumber): boolean; + + /** + * Checks if the first number is less than or equal to the second. + */ + lesserOrEquals(number: BigNumber): boolean; + + /** + * Alias for the lesser method. + */ + lt(number: BigNumber): boolean; + + /** + * Alias for the subtract method. + */ + minus(number: BigNumber): BigInteger; + + /** + * Performs division and returns the remainder, disregarding the quotient. + * The sign of the remainder will match the sign of the dividend. + */ + mod(number: BigNumber): BigInteger; + + /** + * Finds the multiplicative inverse of the number modulo mod. + */ + modInv(number: BigNumber): BigInteger; + + /** + * Takes the number to the power exp modulo mod. + */ + modPow(exp: BigNumber, mod: BigNumber): BigInteger; + + /** + * Performs multiplication. + */ + multiply(number: BigNumber): BigInteger; + + /** + * Reverses the sign of the number. + */ + negate(): BigInteger; + + /** + * Alias for the notEquals method. + */ + neq(number: BigNumber): boolean; + + /** + * Adds one to the number. + */ + next(): BigInteger; + + /** + * Performs the bitwise NOT operation. + */ + not(): BigInteger; + + /** + * Checks if two numbers are not equal. + */ + notEquals(number: BigNumber): boolean; + + /** + * Performs the bitwise OR operation. + */ + or(number: BigNumber): BigInteger; + + /** + * Alias for the divide method. + */ + over(number: BigNumber): BigInteger; + + /** + * Alias for the add method. + */ + plus(number: BigNumber): BigInteger; + + /** + * Performs exponentiation. If the exponent is less than 0, pow returns 0. + * bigInt.zero.pow(0) returns 1. + */ + pow(number: BigNumber): BigInteger; + + /** + * Subtracts one from the number. + */ + prev(): BigInteger; + + /** + * Alias for the mod method. + */ + remainder(number: BigNumber): BigInteger; + + /** + * Shifts the number left by n places in its binary representation. + * If a negative number is provided, it will shift right. + * + * Throws an error if number is outside of the range [-9007199254740992, 9007199254740992]. + */ + shiftLeft(number: BigNumber): BigInteger; + + /** + * Shifts the number right by n places in its binary representation. + * If a negative number is provided, it will shift left. + * + * Throws an error if number is outside of the range [-9007199254740992, 9007199254740992]. + */ + shiftRight(number: BigNumber): BigInteger; + + /** + * Squares the number. + */ + square(): BigInteger; + + /** + * Performs subtraction. + */ + subtract(number: BigNumber): BigInteger; + + /** + * Alias for the multiply method. + */ + times(number: BigNumber): BigInteger; + + /** + * + * Converts a bigInt to an object representing it as an array of integers module the given radix. + */ + toArray(radix: number): BaseArray; + + /** + * Converts a bigInt into a native Javascript number. Loses precision for numbers outside the range. + */ + toJSNumber(): number; + + /** + * Converts a bigInt to a string. + */ + toString(radix?: number, alphabet?: string): string; + + /** + * Converts a bigInt to a string. This method is called behind the scenes in JSON.stringify. + */ + toJSON(): string; + + /** + * Converts a bigInt to a native Javascript number. This override allows you to use native + * arithmetic operators without explicit conversion. + */ + valueOf(): number; + + /** + * Performs the bitwise XOR operation. + */ + xor(number: BigNumber): BigInteger; + } + + // Array constant accessors + interface BigIntegerStatic { + '-999': BigInteger; + '-998': BigInteger; + '-997': BigInteger; + '-996': BigInteger; + '-995': BigInteger; + '-994': BigInteger; + '-993': BigInteger; + '-992': BigInteger; + '-991': BigInteger; + '-990': BigInteger; + '-989': BigInteger; + '-988': BigInteger; + '-987': BigInteger; + '-986': BigInteger; + '-985': BigInteger; + '-984': BigInteger; + '-983': BigInteger; + '-982': BigInteger; + '-981': BigInteger; + '-980': BigInteger; + '-979': BigInteger; + '-978': BigInteger; + '-977': BigInteger; + '-976': BigInteger; + '-975': BigInteger; + '-974': BigInteger; + '-973': BigInteger; + '-972': BigInteger; + '-971': BigInteger; + '-970': BigInteger; + '-969': BigInteger; + '-968': BigInteger; + '-967': BigInteger; + '-966': BigInteger; + '-965': BigInteger; + '-964': BigInteger; + '-963': BigInteger; + '-962': BigInteger; + '-961': BigInteger; + '-960': BigInteger; + '-959': BigInteger; + '-958': BigInteger; + '-957': BigInteger; + '-956': BigInteger; + '-955': BigInteger; + '-954': BigInteger; + '-953': BigInteger; + '-952': BigInteger; + '-951': BigInteger; + '-950': BigInteger; + '-949': BigInteger; + '-948': BigInteger; + '-947': BigInteger; + '-946': BigInteger; + '-945': BigInteger; + '-944': BigInteger; + '-943': BigInteger; + '-942': BigInteger; + '-941': BigInteger; + '-940': BigInteger; + '-939': BigInteger; + '-938': BigInteger; + '-937': BigInteger; + '-936': BigInteger; + '-935': BigInteger; + '-934': BigInteger; + '-933': BigInteger; + '-932': BigInteger; + '-931': BigInteger; + '-930': BigInteger; + '-929': BigInteger; + '-928': BigInteger; + '-927': BigInteger; + '-926': BigInteger; + '-925': BigInteger; + '-924': BigInteger; + '-923': BigInteger; + '-922': BigInteger; + '-921': BigInteger; + '-920': BigInteger; + '-919': BigInteger; + '-918': BigInteger; + '-917': BigInteger; + '-916': BigInteger; + '-915': BigInteger; + '-914': BigInteger; + '-913': BigInteger; + '-912': BigInteger; + '-911': BigInteger; + '-910': BigInteger; + '-909': BigInteger; + '-908': BigInteger; + '-907': BigInteger; + '-906': BigInteger; + '-905': BigInteger; + '-904': BigInteger; + '-903': BigInteger; + '-902': BigInteger; + '-901': BigInteger; + '-900': BigInteger; + '-899': BigInteger; + '-898': BigInteger; + '-897': BigInteger; + '-896': BigInteger; + '-895': BigInteger; + '-894': BigInteger; + '-893': BigInteger; + '-892': BigInteger; + '-891': BigInteger; + '-890': BigInteger; + '-889': BigInteger; + '-888': BigInteger; + '-887': BigInteger; + '-886': BigInteger; + '-885': BigInteger; + '-884': BigInteger; + '-883': BigInteger; + '-882': BigInteger; + '-881': BigInteger; + '-880': BigInteger; + '-879': BigInteger; + '-878': BigInteger; + '-877': BigInteger; + '-876': BigInteger; + '-875': BigInteger; + '-874': BigInteger; + '-873': BigInteger; + '-872': BigInteger; + '-871': BigInteger; + '-870': BigInteger; + '-869': BigInteger; + '-868': BigInteger; + '-867': BigInteger; + '-866': BigInteger; + '-865': BigInteger; + '-864': BigInteger; + '-863': BigInteger; + '-862': BigInteger; + '-861': BigInteger; + '-860': BigInteger; + '-859': BigInteger; + '-858': BigInteger; + '-857': BigInteger; + '-856': BigInteger; + '-855': BigInteger; + '-854': BigInteger; + '-853': BigInteger; + '-852': BigInteger; + '-851': BigInteger; + '-850': BigInteger; + '-849': BigInteger; + '-848': BigInteger; + '-847': BigInteger; + '-846': BigInteger; + '-845': BigInteger; + '-844': BigInteger; + '-843': BigInteger; + '-842': BigInteger; + '-841': BigInteger; + '-840': BigInteger; + '-839': BigInteger; + '-838': BigInteger; + '-837': BigInteger; + '-836': BigInteger; + '-835': BigInteger; + '-834': BigInteger; + '-833': BigInteger; + '-832': BigInteger; + '-831': BigInteger; + '-830': BigInteger; + '-829': BigInteger; + '-828': BigInteger; + '-827': BigInteger; + '-826': BigInteger; + '-825': BigInteger; + '-824': BigInteger; + '-823': BigInteger; + '-822': BigInteger; + '-821': BigInteger; + '-820': BigInteger; + '-819': BigInteger; + '-818': BigInteger; + '-817': BigInteger; + '-816': BigInteger; + '-815': BigInteger; + '-814': BigInteger; + '-813': BigInteger; + '-812': BigInteger; + '-811': BigInteger; + '-810': BigInteger; + '-809': BigInteger; + '-808': BigInteger; + '-807': BigInteger; + '-806': BigInteger; + '-805': BigInteger; + '-804': BigInteger; + '-803': BigInteger; + '-802': BigInteger; + '-801': BigInteger; + '-800': BigInteger; + '-799': BigInteger; + '-798': BigInteger; + '-797': BigInteger; + '-796': BigInteger; + '-795': BigInteger; + '-794': BigInteger; + '-793': BigInteger; + '-792': BigInteger; + '-791': BigInteger; + '-790': BigInteger; + '-789': BigInteger; + '-788': BigInteger; + '-787': BigInteger; + '-786': BigInteger; + '-785': BigInteger; + '-784': BigInteger; + '-783': BigInteger; + '-782': BigInteger; + '-781': BigInteger; + '-780': BigInteger; + '-779': BigInteger; + '-778': BigInteger; + '-777': BigInteger; + '-776': BigInteger; + '-775': BigInteger; + '-774': BigInteger; + '-773': BigInteger; + '-772': BigInteger; + '-771': BigInteger; + '-770': BigInteger; + '-769': BigInteger; + '-768': BigInteger; + '-767': BigInteger; + '-766': BigInteger; + '-765': BigInteger; + '-764': BigInteger; + '-763': BigInteger; + '-762': BigInteger; + '-761': BigInteger; + '-760': BigInteger; + '-759': BigInteger; + '-758': BigInteger; + '-757': BigInteger; + '-756': BigInteger; + '-755': BigInteger; + '-754': BigInteger; + '-753': BigInteger; + '-752': BigInteger; + '-751': BigInteger; + '-750': BigInteger; + '-749': BigInteger; + '-748': BigInteger; + '-747': BigInteger; + '-746': BigInteger; + '-745': BigInteger; + '-744': BigInteger; + '-743': BigInteger; + '-742': BigInteger; + '-741': BigInteger; + '-740': BigInteger; + '-739': BigInteger; + '-738': BigInteger; + '-737': BigInteger; + '-736': BigInteger; + '-735': BigInteger; + '-734': BigInteger; + '-733': BigInteger; + '-732': BigInteger; + '-731': BigInteger; + '-730': BigInteger; + '-729': BigInteger; + '-728': BigInteger; + '-727': BigInteger; + '-726': BigInteger; + '-725': BigInteger; + '-724': BigInteger; + '-723': BigInteger; + '-722': BigInteger; + '-721': BigInteger; + '-720': BigInteger; + '-719': BigInteger; + '-718': BigInteger; + '-717': BigInteger; + '-716': BigInteger; + '-715': BigInteger; + '-714': BigInteger; + '-713': BigInteger; + '-712': BigInteger; + '-711': BigInteger; + '-710': BigInteger; + '-709': BigInteger; + '-708': BigInteger; + '-707': BigInteger; + '-706': BigInteger; + '-705': BigInteger; + '-704': BigInteger; + '-703': BigInteger; + '-702': BigInteger; + '-701': BigInteger; + '-700': BigInteger; + '-699': BigInteger; + '-698': BigInteger; + '-697': BigInteger; + '-696': BigInteger; + '-695': BigInteger; + '-694': BigInteger; + '-693': BigInteger; + '-692': BigInteger; + '-691': BigInteger; + '-690': BigInteger; + '-689': BigInteger; + '-688': BigInteger; + '-687': BigInteger; + '-686': BigInteger; + '-685': BigInteger; + '-684': BigInteger; + '-683': BigInteger; + '-682': BigInteger; + '-681': BigInteger; + '-680': BigInteger; + '-679': BigInteger; + '-678': BigInteger; + '-677': BigInteger; + '-676': BigInteger; + '-675': BigInteger; + '-674': BigInteger; + '-673': BigInteger; + '-672': BigInteger; + '-671': BigInteger; + '-670': BigInteger; + '-669': BigInteger; + '-668': BigInteger; + '-667': BigInteger; + '-666': BigInteger; + '-665': BigInteger; + '-664': BigInteger; + '-663': BigInteger; + '-662': BigInteger; + '-661': BigInteger; + '-660': BigInteger; + '-659': BigInteger; + '-658': BigInteger; + '-657': BigInteger; + '-656': BigInteger; + '-655': BigInteger; + '-654': BigInteger; + '-653': BigInteger; + '-652': BigInteger; + '-651': BigInteger; + '-650': BigInteger; + '-649': BigInteger; + '-648': BigInteger; + '-647': BigInteger; + '-646': BigInteger; + '-645': BigInteger; + '-644': BigInteger; + '-643': BigInteger; + '-642': BigInteger; + '-641': BigInteger; + '-640': BigInteger; + '-639': BigInteger; + '-638': BigInteger; + '-637': BigInteger; + '-636': BigInteger; + '-635': BigInteger; + '-634': BigInteger; + '-633': BigInteger; + '-632': BigInteger; + '-631': BigInteger; + '-630': BigInteger; + '-629': BigInteger; + '-628': BigInteger; + '-627': BigInteger; + '-626': BigInteger; + '-625': BigInteger; + '-624': BigInteger; + '-623': BigInteger; + '-622': BigInteger; + '-621': BigInteger; + '-620': BigInteger; + '-619': BigInteger; + '-618': BigInteger; + '-617': BigInteger; + '-616': BigInteger; + '-615': BigInteger; + '-614': BigInteger; + '-613': BigInteger; + '-612': BigInteger; + '-611': BigInteger; + '-610': BigInteger; + '-609': BigInteger; + '-608': BigInteger; + '-607': BigInteger; + '-606': BigInteger; + '-605': BigInteger; + '-604': BigInteger; + '-603': BigInteger; + '-602': BigInteger; + '-601': BigInteger; + '-600': BigInteger; + '-599': BigInteger; + '-598': BigInteger; + '-597': BigInteger; + '-596': BigInteger; + '-595': BigInteger; + '-594': BigInteger; + '-593': BigInteger; + '-592': BigInteger; + '-591': BigInteger; + '-590': BigInteger; + '-589': BigInteger; + '-588': BigInteger; + '-587': BigInteger; + '-586': BigInteger; + '-585': BigInteger; + '-584': BigInteger; + '-583': BigInteger; + '-582': BigInteger; + '-581': BigInteger; + '-580': BigInteger; + '-579': BigInteger; + '-578': BigInteger; + '-577': BigInteger; + '-576': BigInteger; + '-575': BigInteger; + '-574': BigInteger; + '-573': BigInteger; + '-572': BigInteger; + '-571': BigInteger; + '-570': BigInteger; + '-569': BigInteger; + '-568': BigInteger; + '-567': BigInteger; + '-566': BigInteger; + '-565': BigInteger; + '-564': BigInteger; + '-563': BigInteger; + '-562': BigInteger; + '-561': BigInteger; + '-560': BigInteger; + '-559': BigInteger; + '-558': BigInteger; + '-557': BigInteger; + '-556': BigInteger; + '-555': BigInteger; + '-554': BigInteger; + '-553': BigInteger; + '-552': BigInteger; + '-551': BigInteger; + '-550': BigInteger; + '-549': BigInteger; + '-548': BigInteger; + '-547': BigInteger; + '-546': BigInteger; + '-545': BigInteger; + '-544': BigInteger; + '-543': BigInteger; + '-542': BigInteger; + '-541': BigInteger; + '-540': BigInteger; + '-539': BigInteger; + '-538': BigInteger; + '-537': BigInteger; + '-536': BigInteger; + '-535': BigInteger; + '-534': BigInteger; + '-533': BigInteger; + '-532': BigInteger; + '-531': BigInteger; + '-530': BigInteger; + '-529': BigInteger; + '-528': BigInteger; + '-527': BigInteger; + '-526': BigInteger; + '-525': BigInteger; + '-524': BigInteger; + '-523': BigInteger; + '-522': BigInteger; + '-521': BigInteger; + '-520': BigInteger; + '-519': BigInteger; + '-518': BigInteger; + '-517': BigInteger; + '-516': BigInteger; + '-515': BigInteger; + '-514': BigInteger; + '-513': BigInteger; + '-512': BigInteger; + '-511': BigInteger; + '-510': BigInteger; + '-509': BigInteger; + '-508': BigInteger; + '-507': BigInteger; + '-506': BigInteger; + '-505': BigInteger; + '-504': BigInteger; + '-503': BigInteger; + '-502': BigInteger; + '-501': BigInteger; + '-500': BigInteger; + '-499': BigInteger; + '-498': BigInteger; + '-497': BigInteger; + '-496': BigInteger; + '-495': BigInteger; + '-494': BigInteger; + '-493': BigInteger; + '-492': BigInteger; + '-491': BigInteger; + '-490': BigInteger; + '-489': BigInteger; + '-488': BigInteger; + '-487': BigInteger; + '-486': BigInteger; + '-485': BigInteger; + '-484': BigInteger; + '-483': BigInteger; + '-482': BigInteger; + '-481': BigInteger; + '-480': BigInteger; + '-479': BigInteger; + '-478': BigInteger; + '-477': BigInteger; + '-476': BigInteger; + '-475': BigInteger; + '-474': BigInteger; + '-473': BigInteger; + '-472': BigInteger; + '-471': BigInteger; + '-470': BigInteger; + '-469': BigInteger; + '-468': BigInteger; + '-467': BigInteger; + '-466': BigInteger; + '-465': BigInteger; + '-464': BigInteger; + '-463': BigInteger; + '-462': BigInteger; + '-461': BigInteger; + '-460': BigInteger; + '-459': BigInteger; + '-458': BigInteger; + '-457': BigInteger; + '-456': BigInteger; + '-455': BigInteger; + '-454': BigInteger; + '-453': BigInteger; + '-452': BigInteger; + '-451': BigInteger; + '-450': BigInteger; + '-449': BigInteger; + '-448': BigInteger; + '-447': BigInteger; + '-446': BigInteger; + '-445': BigInteger; + '-444': BigInteger; + '-443': BigInteger; + '-442': BigInteger; + '-441': BigInteger; + '-440': BigInteger; + '-439': BigInteger; + '-438': BigInteger; + '-437': BigInteger; + '-436': BigInteger; + '-435': BigInteger; + '-434': BigInteger; + '-433': BigInteger; + '-432': BigInteger; + '-431': BigInteger; + '-430': BigInteger; + '-429': BigInteger; + '-428': BigInteger; + '-427': BigInteger; + '-426': BigInteger; + '-425': BigInteger; + '-424': BigInteger; + '-423': BigInteger; + '-422': BigInteger; + '-421': BigInteger; + '-420': BigInteger; + '-419': BigInteger; + '-418': BigInteger; + '-417': BigInteger; + '-416': BigInteger; + '-415': BigInteger; + '-414': BigInteger; + '-413': BigInteger; + '-412': BigInteger; + '-411': BigInteger; + '-410': BigInteger; + '-409': BigInteger; + '-408': BigInteger; + '-407': BigInteger; + '-406': BigInteger; + '-405': BigInteger; + '-404': BigInteger; + '-403': BigInteger; + '-402': BigInteger; + '-401': BigInteger; + '-400': BigInteger; + '-399': BigInteger; + '-398': BigInteger; + '-397': BigInteger; + '-396': BigInteger; + '-395': BigInteger; + '-394': BigInteger; + '-393': BigInteger; + '-392': BigInteger; + '-391': BigInteger; + '-390': BigInteger; + '-389': BigInteger; + '-388': BigInteger; + '-387': BigInteger; + '-386': BigInteger; + '-385': BigInteger; + '-384': BigInteger; + '-383': BigInteger; + '-382': BigInteger; + '-381': BigInteger; + '-380': BigInteger; + '-379': BigInteger; + '-378': BigInteger; + '-377': BigInteger; + '-376': BigInteger; + '-375': BigInteger; + '-374': BigInteger; + '-373': BigInteger; + '-372': BigInteger; + '-371': BigInteger; + '-370': BigInteger; + '-369': BigInteger; + '-368': BigInteger; + '-367': BigInteger; + '-366': BigInteger; + '-365': BigInteger; + '-364': BigInteger; + '-363': BigInteger; + '-362': BigInteger; + '-361': BigInteger; + '-360': BigInteger; + '-359': BigInteger; + '-358': BigInteger; + '-357': BigInteger; + '-356': BigInteger; + '-355': BigInteger; + '-354': BigInteger; + '-353': BigInteger; + '-352': BigInteger; + '-351': BigInteger; + '-350': BigInteger; + '-349': BigInteger; + '-348': BigInteger; + '-347': BigInteger; + '-346': BigInteger; + '-345': BigInteger; + '-344': BigInteger; + '-343': BigInteger; + '-342': BigInteger; + '-341': BigInteger; + '-340': BigInteger; + '-339': BigInteger; + '-338': BigInteger; + '-337': BigInteger; + '-336': BigInteger; + '-335': BigInteger; + '-334': BigInteger; + '-333': BigInteger; + '-332': BigInteger; + '-331': BigInteger; + '-330': BigInteger; + '-329': BigInteger; + '-328': BigInteger; + '-327': BigInteger; + '-326': BigInteger; + '-325': BigInteger; + '-324': BigInteger; + '-323': BigInteger; + '-322': BigInteger; + '-321': BigInteger; + '-320': BigInteger; + '-319': BigInteger; + '-318': BigInteger; + '-317': BigInteger; + '-316': BigInteger; + '-315': BigInteger; + '-314': BigInteger; + '-313': BigInteger; + '-312': BigInteger; + '-311': BigInteger; + '-310': BigInteger; + '-309': BigInteger; + '-308': BigInteger; + '-307': BigInteger; + '-306': BigInteger; + '-305': BigInteger; + '-304': BigInteger; + '-303': BigInteger; + '-302': BigInteger; + '-301': BigInteger; + '-300': BigInteger; + '-299': BigInteger; + '-298': BigInteger; + '-297': BigInteger; + '-296': BigInteger; + '-295': BigInteger; + '-294': BigInteger; + '-293': BigInteger; + '-292': BigInteger; + '-291': BigInteger; + '-290': BigInteger; + '-289': BigInteger; + '-288': BigInteger; + '-287': BigInteger; + '-286': BigInteger; + '-285': BigInteger; + '-284': BigInteger; + '-283': BigInteger; + '-282': BigInteger; + '-281': BigInteger; + '-280': BigInteger; + '-279': BigInteger; + '-278': BigInteger; + '-277': BigInteger; + '-276': BigInteger; + '-275': BigInteger; + '-274': BigInteger; + '-273': BigInteger; + '-272': BigInteger; + '-271': BigInteger; + '-270': BigInteger; + '-269': BigInteger; + '-268': BigInteger; + '-267': BigInteger; + '-266': BigInteger; + '-265': BigInteger; + '-264': BigInteger; + '-263': BigInteger; + '-262': BigInteger; + '-261': BigInteger; + '-260': BigInteger; + '-259': BigInteger; + '-258': BigInteger; + '-257': BigInteger; + '-256': BigInteger; + '-255': BigInteger; + '-254': BigInteger; + '-253': BigInteger; + '-252': BigInteger; + '-251': BigInteger; + '-250': BigInteger; + '-249': BigInteger; + '-248': BigInteger; + '-247': BigInteger; + '-246': BigInteger; + '-245': BigInteger; + '-244': BigInteger; + '-243': BigInteger; + '-242': BigInteger; + '-241': BigInteger; + '-240': BigInteger; + '-239': BigInteger; + '-238': BigInteger; + '-237': BigInteger; + '-236': BigInteger; + '-235': BigInteger; + '-234': BigInteger; + '-233': BigInteger; + '-232': BigInteger; + '-231': BigInteger; + '-230': BigInteger; + '-229': BigInteger; + '-228': BigInteger; + '-227': BigInteger; + '-226': BigInteger; + '-225': BigInteger; + '-224': BigInteger; + '-223': BigInteger; + '-222': BigInteger; + '-221': BigInteger; + '-220': BigInteger; + '-219': BigInteger; + '-218': BigInteger; + '-217': BigInteger; + '-216': BigInteger; + '-215': BigInteger; + '-214': BigInteger; + '-213': BigInteger; + '-212': BigInteger; + '-211': BigInteger; + '-210': BigInteger; + '-209': BigInteger; + '-208': BigInteger; + '-207': BigInteger; + '-206': BigInteger; + '-205': BigInteger; + '-204': BigInteger; + '-203': BigInteger; + '-202': BigInteger; + '-201': BigInteger; + '-200': BigInteger; + '-199': BigInteger; + '-198': BigInteger; + '-197': BigInteger; + '-196': BigInteger; + '-195': BigInteger; + '-194': BigInteger; + '-193': BigInteger; + '-192': BigInteger; + '-191': BigInteger; + '-190': BigInteger; + '-189': BigInteger; + '-188': BigInteger; + '-187': BigInteger; + '-186': BigInteger; + '-185': BigInteger; + '-184': BigInteger; + '-183': BigInteger; + '-182': BigInteger; + '-181': BigInteger; + '-180': BigInteger; + '-179': BigInteger; + '-178': BigInteger; + '-177': BigInteger; + '-176': BigInteger; + '-175': BigInteger; + '-174': BigInteger; + '-173': BigInteger; + '-172': BigInteger; + '-171': BigInteger; + '-170': BigInteger; + '-169': BigInteger; + '-168': BigInteger; + '-167': BigInteger; + '-166': BigInteger; + '-165': BigInteger; + '-164': BigInteger; + '-163': BigInteger; + '-162': BigInteger; + '-161': BigInteger; + '-160': BigInteger; + '-159': BigInteger; + '-158': BigInteger; + '-157': BigInteger; + '-156': BigInteger; + '-155': BigInteger; + '-154': BigInteger; + '-153': BigInteger; + '-152': BigInteger; + '-151': BigInteger; + '-150': BigInteger; + '-149': BigInteger; + '-148': BigInteger; + '-147': BigInteger; + '-146': BigInteger; + '-145': BigInteger; + '-144': BigInteger; + '-143': BigInteger; + '-142': BigInteger; + '-141': BigInteger; + '-140': BigInteger; + '-139': BigInteger; + '-138': BigInteger; + '-137': BigInteger; + '-136': BigInteger; + '-135': BigInteger; + '-134': BigInteger; + '-133': BigInteger; + '-132': BigInteger; + '-131': BigInteger; + '-130': BigInteger; + '-129': BigInteger; + '-128': BigInteger; + '-127': BigInteger; + '-126': BigInteger; + '-125': BigInteger; + '-124': BigInteger; + '-123': BigInteger; + '-122': BigInteger; + '-121': BigInteger; + '-120': BigInteger; + '-119': BigInteger; + '-118': BigInteger; + '-117': BigInteger; + '-116': BigInteger; + '-115': BigInteger; + '-114': BigInteger; + '-113': BigInteger; + '-112': BigInteger; + '-111': BigInteger; + '-110': BigInteger; + '-109': BigInteger; + '-108': BigInteger; + '-107': BigInteger; + '-106': BigInteger; + '-105': BigInteger; + '-104': BigInteger; + '-103': BigInteger; + '-102': BigInteger; + '-101': BigInteger; + '-100': BigInteger; + '-99': BigInteger; + '-98': BigInteger; + '-97': BigInteger; + '-96': BigInteger; + '-95': BigInteger; + '-94': BigInteger; + '-93': BigInteger; + '-92': BigInteger; + '-91': BigInteger; + '-90': BigInteger; + '-89': BigInteger; + '-88': BigInteger; + '-87': BigInteger; + '-86': BigInteger; + '-85': BigInteger; + '-84': BigInteger; + '-83': BigInteger; + '-82': BigInteger; + '-81': BigInteger; + '-80': BigInteger; + '-79': BigInteger; + '-78': BigInteger; + '-77': BigInteger; + '-76': BigInteger; + '-75': BigInteger; + '-74': BigInteger; + '-73': BigInteger; + '-72': BigInteger; + '-71': BigInteger; + '-70': BigInteger; + '-69': BigInteger; + '-68': BigInteger; + '-67': BigInteger; + '-66': BigInteger; + '-65': BigInteger; + '-64': BigInteger; + '-63': BigInteger; + '-62': BigInteger; + '-61': BigInteger; + '-60': BigInteger; + '-59': BigInteger; + '-58': BigInteger; + '-57': BigInteger; + '-56': BigInteger; + '-55': BigInteger; + '-54': BigInteger; + '-53': BigInteger; + '-52': BigInteger; + '-51': BigInteger; + '-50': BigInteger; + '-49': BigInteger; + '-48': BigInteger; + '-47': BigInteger; + '-46': BigInteger; + '-45': BigInteger; + '-44': BigInteger; + '-43': BigInteger; + '-42': BigInteger; + '-41': BigInteger; + '-40': BigInteger; + '-39': BigInteger; + '-38': BigInteger; + '-37': BigInteger; + '-36': BigInteger; + '-35': BigInteger; + '-34': BigInteger; + '-33': BigInteger; + '-32': BigInteger; + '-31': BigInteger; + '-30': BigInteger; + '-29': BigInteger; + '-28': BigInteger; + '-27': BigInteger; + '-26': BigInteger; + '-25': BigInteger; + '-24': BigInteger; + '-23': BigInteger; + '-22': BigInteger; + '-21': BigInteger; + '-20': BigInteger; + '-19': BigInteger; + '-18': BigInteger; + '-17': BigInteger; + '-16': BigInteger; + '-15': BigInteger; + '-14': BigInteger; + '-13': BigInteger; + '-12': BigInteger; + '-11': BigInteger; + '-10': BigInteger; + '-9': BigInteger; + '-8': BigInteger; + '-7': BigInteger; + '-6': BigInteger; + '-5': BigInteger; + '-4': BigInteger; + '-3': BigInteger; + '-2': BigInteger; + '-1': BigInteger; + '0': BigInteger; + '1': BigInteger; + '2': BigInteger; + '3': BigInteger; + '4': BigInteger; + '5': BigInteger; + '6': BigInteger; + '7': BigInteger; + '8': BigInteger; + '9': BigInteger; + '10': BigInteger; + '11': BigInteger; + '12': BigInteger; + '13': BigInteger; + '14': BigInteger; + '15': BigInteger; + '16': BigInteger; + '17': BigInteger; + '18': BigInteger; + '19': BigInteger; + '20': BigInteger; + '21': BigInteger; + '22': BigInteger; + '23': BigInteger; + '24': BigInteger; + '25': BigInteger; + '26': BigInteger; + '27': BigInteger; + '28': BigInteger; + '29': BigInteger; + '30': BigInteger; + '31': BigInteger; + '32': BigInteger; + '33': BigInteger; + '34': BigInteger; + '35': BigInteger; + '36': BigInteger; + '37': BigInteger; + '38': BigInteger; + '39': BigInteger; + '40': BigInteger; + '41': BigInteger; + '42': BigInteger; + '43': BigInteger; + '44': BigInteger; + '45': BigInteger; + '46': BigInteger; + '47': BigInteger; + '48': BigInteger; + '49': BigInteger; + '50': BigInteger; + '51': BigInteger; + '52': BigInteger; + '53': BigInteger; + '54': BigInteger; + '55': BigInteger; + '56': BigInteger; + '57': BigInteger; + '58': BigInteger; + '59': BigInteger; + '60': BigInteger; + '61': BigInteger; + '62': BigInteger; + '63': BigInteger; + '64': BigInteger; + '65': BigInteger; + '66': BigInteger; + '67': BigInteger; + '68': BigInteger; + '69': BigInteger; + '70': BigInteger; + '71': BigInteger; + '72': BigInteger; + '73': BigInteger; + '74': BigInteger; + '75': BigInteger; + '76': BigInteger; + '77': BigInteger; + '78': BigInteger; + '79': BigInteger; + '80': BigInteger; + '81': BigInteger; + '82': BigInteger; + '83': BigInteger; + '84': BigInteger; + '85': BigInteger; + '86': BigInteger; + '87': BigInteger; + '88': BigInteger; + '89': BigInteger; + '90': BigInteger; + '91': BigInteger; + '92': BigInteger; + '93': BigInteger; + '94': BigInteger; + '95': BigInteger; + '96': BigInteger; + '97': BigInteger; + '98': BigInteger; + '99': BigInteger; + '100': BigInteger; + '101': BigInteger; + '102': BigInteger; + '103': BigInteger; + '104': BigInteger; + '105': BigInteger; + '106': BigInteger; + '107': BigInteger; + '108': BigInteger; + '109': BigInteger; + '110': BigInteger; + '111': BigInteger; + '112': BigInteger; + '113': BigInteger; + '114': BigInteger; + '115': BigInteger; + '116': BigInteger; + '117': BigInteger; + '118': BigInteger; + '119': BigInteger; + '120': BigInteger; + '121': BigInteger; + '122': BigInteger; + '123': BigInteger; + '124': BigInteger; + '125': BigInteger; + '126': BigInteger; + '127': BigInteger; + '128': BigInteger; + '129': BigInteger; + '130': BigInteger; + '131': BigInteger; + '132': BigInteger; + '133': BigInteger; + '134': BigInteger; + '135': BigInteger; + '136': BigInteger; + '137': BigInteger; + '138': BigInteger; + '139': BigInteger; + '140': BigInteger; + '141': BigInteger; + '142': BigInteger; + '143': BigInteger; + '144': BigInteger; + '145': BigInteger; + '146': BigInteger; + '147': BigInteger; + '148': BigInteger; + '149': BigInteger; + '150': BigInteger; + '151': BigInteger; + '152': BigInteger; + '153': BigInteger; + '154': BigInteger; + '155': BigInteger; + '156': BigInteger; + '157': BigInteger; + '158': BigInteger; + '159': BigInteger; + '160': BigInteger; + '161': BigInteger; + '162': BigInteger; + '163': BigInteger; + '164': BigInteger; + '165': BigInteger; + '166': BigInteger; + '167': BigInteger; + '168': BigInteger; + '169': BigInteger; + '170': BigInteger; + '171': BigInteger; + '172': BigInteger; + '173': BigInteger; + '174': BigInteger; + '175': BigInteger; + '176': BigInteger; + '177': BigInteger; + '178': BigInteger; + '179': BigInteger; + '180': BigInteger; + '181': BigInteger; + '182': BigInteger; + '183': BigInteger; + '184': BigInteger; + '185': BigInteger; + '186': BigInteger; + '187': BigInteger; + '188': BigInteger; + '189': BigInteger; + '190': BigInteger; + '191': BigInteger; + '192': BigInteger; + '193': BigInteger; + '194': BigInteger; + '195': BigInteger; + '196': BigInteger; + '197': BigInteger; + '198': BigInteger; + '199': BigInteger; + '200': BigInteger; + '201': BigInteger; + '202': BigInteger; + '203': BigInteger; + '204': BigInteger; + '205': BigInteger; + '206': BigInteger; + '207': BigInteger; + '208': BigInteger; + '209': BigInteger; + '210': BigInteger; + '211': BigInteger; + '212': BigInteger; + '213': BigInteger; + '214': BigInteger; + '215': BigInteger; + '216': BigInteger; + '217': BigInteger; + '218': BigInteger; + '219': BigInteger; + '220': BigInteger; + '221': BigInteger; + '222': BigInteger; + '223': BigInteger; + '224': BigInteger; + '225': BigInteger; + '226': BigInteger; + '227': BigInteger; + '228': BigInteger; + '229': BigInteger; + '230': BigInteger; + '231': BigInteger; + '232': BigInteger; + '233': BigInteger; + '234': BigInteger; + '235': BigInteger; + '236': BigInteger; + '237': BigInteger; + '238': BigInteger; + '239': BigInteger; + '240': BigInteger; + '241': BigInteger; + '242': BigInteger; + '243': BigInteger; + '244': BigInteger; + '245': BigInteger; + '246': BigInteger; + '247': BigInteger; + '248': BigInteger; + '249': BigInteger; + '250': BigInteger; + '251': BigInteger; + '252': BigInteger; + '253': BigInteger; + '254': BigInteger; + '255': BigInteger; + '256': BigInteger; + '257': BigInteger; + '258': BigInteger; + '259': BigInteger; + '260': BigInteger; + '261': BigInteger; + '262': BigInteger; + '263': BigInteger; + '264': BigInteger; + '265': BigInteger; + '266': BigInteger; + '267': BigInteger; + '268': BigInteger; + '269': BigInteger; + '270': BigInteger; + '271': BigInteger; + '272': BigInteger; + '273': BigInteger; + '274': BigInteger; + '275': BigInteger; + '276': BigInteger; + '277': BigInteger; + '278': BigInteger; + '279': BigInteger; + '280': BigInteger; + '281': BigInteger; + '282': BigInteger; + '283': BigInteger; + '284': BigInteger; + '285': BigInteger; + '286': BigInteger; + '287': BigInteger; + '288': BigInteger; + '289': BigInteger; + '290': BigInteger; + '291': BigInteger; + '292': BigInteger; + '293': BigInteger; + '294': BigInteger; + '295': BigInteger; + '296': BigInteger; + '297': BigInteger; + '298': BigInteger; + '299': BigInteger; + '300': BigInteger; + '301': BigInteger; + '302': BigInteger; + '303': BigInteger; + '304': BigInteger; + '305': BigInteger; + '306': BigInteger; + '307': BigInteger; + '308': BigInteger; + '309': BigInteger; + '310': BigInteger; + '311': BigInteger; + '312': BigInteger; + '313': BigInteger; + '314': BigInteger; + '315': BigInteger; + '316': BigInteger; + '317': BigInteger; + '318': BigInteger; + '319': BigInteger; + '320': BigInteger; + '321': BigInteger; + '322': BigInteger; + '323': BigInteger; + '324': BigInteger; + '325': BigInteger; + '326': BigInteger; + '327': BigInteger; + '328': BigInteger; + '329': BigInteger; + '330': BigInteger; + '331': BigInteger; + '332': BigInteger; + '333': BigInteger; + '334': BigInteger; + '335': BigInteger; + '336': BigInteger; + '337': BigInteger; + '338': BigInteger; + '339': BigInteger; + '340': BigInteger; + '341': BigInteger; + '342': BigInteger; + '343': BigInteger; + '344': BigInteger; + '345': BigInteger; + '346': BigInteger; + '347': BigInteger; + '348': BigInteger; + '349': BigInteger; + '350': BigInteger; + '351': BigInteger; + '352': BigInteger; + '353': BigInteger; + '354': BigInteger; + '355': BigInteger; + '356': BigInteger; + '357': BigInteger; + '358': BigInteger; + '359': BigInteger; + '360': BigInteger; + '361': BigInteger; + '362': BigInteger; + '363': BigInteger; + '364': BigInteger; + '365': BigInteger; + '366': BigInteger; + '367': BigInteger; + '368': BigInteger; + '369': BigInteger; + '370': BigInteger; + '371': BigInteger; + '372': BigInteger; + '373': BigInteger; + '374': BigInteger; + '375': BigInteger; + '376': BigInteger; + '377': BigInteger; + '378': BigInteger; + '379': BigInteger; + '380': BigInteger; + '381': BigInteger; + '382': BigInteger; + '383': BigInteger; + '384': BigInteger; + '385': BigInteger; + '386': BigInteger; + '387': BigInteger; + '388': BigInteger; + '389': BigInteger; + '390': BigInteger; + '391': BigInteger; + '392': BigInteger; + '393': BigInteger; + '394': BigInteger; + '395': BigInteger; + '396': BigInteger; + '397': BigInteger; + '398': BigInteger; + '399': BigInteger; + '400': BigInteger; + '401': BigInteger; + '402': BigInteger; + '403': BigInteger; + '404': BigInteger; + '405': BigInteger; + '406': BigInteger; + '407': BigInteger; + '408': BigInteger; + '409': BigInteger; + '410': BigInteger; + '411': BigInteger; + '412': BigInteger; + '413': BigInteger; + '414': BigInteger; + '415': BigInteger; + '416': BigInteger; + '417': BigInteger; + '418': BigInteger; + '419': BigInteger; + '420': BigInteger; + '421': BigInteger; + '422': BigInteger; + '423': BigInteger; + '424': BigInteger; + '425': BigInteger; + '426': BigInteger; + '427': BigInteger; + '428': BigInteger; + '429': BigInteger; + '430': BigInteger; + '431': BigInteger; + '432': BigInteger; + '433': BigInteger; + '434': BigInteger; + '435': BigInteger; + '436': BigInteger; + '437': BigInteger; + '438': BigInteger; + '439': BigInteger; + '440': BigInteger; + '441': BigInteger; + '442': BigInteger; + '443': BigInteger; + '444': BigInteger; + '445': BigInteger; + '446': BigInteger; + '447': BigInteger; + '448': BigInteger; + '449': BigInteger; + '450': BigInteger; + '451': BigInteger; + '452': BigInteger; + '453': BigInteger; + '454': BigInteger; + '455': BigInteger; + '456': BigInteger; + '457': BigInteger; + '458': BigInteger; + '459': BigInteger; + '460': BigInteger; + '461': BigInteger; + '462': BigInteger; + '463': BigInteger; + '464': BigInteger; + '465': BigInteger; + '466': BigInteger; + '467': BigInteger; + '468': BigInteger; + '469': BigInteger; + '470': BigInteger; + '471': BigInteger; + '472': BigInteger; + '473': BigInteger; + '474': BigInteger; + '475': BigInteger; + '476': BigInteger; + '477': BigInteger; + '478': BigInteger; + '479': BigInteger; + '480': BigInteger; + '481': BigInteger; + '482': BigInteger; + '483': BigInteger; + '484': BigInteger; + '485': BigInteger; + '486': BigInteger; + '487': BigInteger; + '488': BigInteger; + '489': BigInteger; + '490': BigInteger; + '491': BigInteger; + '492': BigInteger; + '493': BigInteger; + '494': BigInteger; + '495': BigInteger; + '496': BigInteger; + '497': BigInteger; + '498': BigInteger; + '499': BigInteger; + '500': BigInteger; + '501': BigInteger; + '502': BigInteger; + '503': BigInteger; + '504': BigInteger; + '505': BigInteger; + '506': BigInteger; + '507': BigInteger; + '508': BigInteger; + '509': BigInteger; + '510': BigInteger; + '511': BigInteger; + '512': BigInteger; + '513': BigInteger; + '514': BigInteger; + '515': BigInteger; + '516': BigInteger; + '517': BigInteger; + '518': BigInteger; + '519': BigInteger; + '520': BigInteger; + '521': BigInteger; + '522': BigInteger; + '523': BigInteger; + '524': BigInteger; + '525': BigInteger; + '526': BigInteger; + '527': BigInteger; + '528': BigInteger; + '529': BigInteger; + '530': BigInteger; + '531': BigInteger; + '532': BigInteger; + '533': BigInteger; + '534': BigInteger; + '535': BigInteger; + '536': BigInteger; + '537': BigInteger; + '538': BigInteger; + '539': BigInteger; + '540': BigInteger; + '541': BigInteger; + '542': BigInteger; + '543': BigInteger; + '544': BigInteger; + '545': BigInteger; + '546': BigInteger; + '547': BigInteger; + '548': BigInteger; + '549': BigInteger; + '550': BigInteger; + '551': BigInteger; + '552': BigInteger; + '553': BigInteger; + '554': BigInteger; + '555': BigInteger; + '556': BigInteger; + '557': BigInteger; + '558': BigInteger; + '559': BigInteger; + '560': BigInteger; + '561': BigInteger; + '562': BigInteger; + '563': BigInteger; + '564': BigInteger; + '565': BigInteger; + '566': BigInteger; + '567': BigInteger; + '568': BigInteger; + '569': BigInteger; + '570': BigInteger; + '571': BigInteger; + '572': BigInteger; + '573': BigInteger; + '574': BigInteger; + '575': BigInteger; + '576': BigInteger; + '577': BigInteger; + '578': BigInteger; + '579': BigInteger; + '580': BigInteger; + '581': BigInteger; + '582': BigInteger; + '583': BigInteger; + '584': BigInteger; + '585': BigInteger; + '586': BigInteger; + '587': BigInteger; + '588': BigInteger; + '589': BigInteger; + '590': BigInteger; + '591': BigInteger; + '592': BigInteger; + '593': BigInteger; + '594': BigInteger; + '595': BigInteger; + '596': BigInteger; + '597': BigInteger; + '598': BigInteger; + '599': BigInteger; + '600': BigInteger; + '601': BigInteger; + '602': BigInteger; + '603': BigInteger; + '604': BigInteger; + '605': BigInteger; + '606': BigInteger; + '607': BigInteger; + '608': BigInteger; + '609': BigInteger; + '610': BigInteger; + '611': BigInteger; + '612': BigInteger; + '613': BigInteger; + '614': BigInteger; + '615': BigInteger; + '616': BigInteger; + '617': BigInteger; + '618': BigInteger; + '619': BigInteger; + '620': BigInteger; + '621': BigInteger; + '622': BigInteger; + '623': BigInteger; + '624': BigInteger; + '625': BigInteger; + '626': BigInteger; + '627': BigInteger; + '628': BigInteger; + '629': BigInteger; + '630': BigInteger; + '631': BigInteger; + '632': BigInteger; + '633': BigInteger; + '634': BigInteger; + '635': BigInteger; + '636': BigInteger; + '637': BigInteger; + '638': BigInteger; + '639': BigInteger; + '640': BigInteger; + '641': BigInteger; + '642': BigInteger; + '643': BigInteger; + '644': BigInteger; + '645': BigInteger; + '646': BigInteger; + '647': BigInteger; + '648': BigInteger; + '649': BigInteger; + '650': BigInteger; + '651': BigInteger; + '652': BigInteger; + '653': BigInteger; + '654': BigInteger; + '655': BigInteger; + '656': BigInteger; + '657': BigInteger; + '658': BigInteger; + '659': BigInteger; + '660': BigInteger; + '661': BigInteger; + '662': BigInteger; + '663': BigInteger; + '664': BigInteger; + '665': BigInteger; + '666': BigInteger; + '667': BigInteger; + '668': BigInteger; + '669': BigInteger; + '670': BigInteger; + '671': BigInteger; + '672': BigInteger; + '673': BigInteger; + '674': BigInteger; + '675': BigInteger; + '676': BigInteger; + '677': BigInteger; + '678': BigInteger; + '679': BigInteger; + '680': BigInteger; + '681': BigInteger; + '682': BigInteger; + '683': BigInteger; + '684': BigInteger; + '685': BigInteger; + '686': BigInteger; + '687': BigInteger; + '688': BigInteger; + '689': BigInteger; + '690': BigInteger; + '691': BigInteger; + '692': BigInteger; + '693': BigInteger; + '694': BigInteger; + '695': BigInteger; + '696': BigInteger; + '697': BigInteger; + '698': BigInteger; + '699': BigInteger; + '700': BigInteger; + '701': BigInteger; + '702': BigInteger; + '703': BigInteger; + '704': BigInteger; + '705': BigInteger; + '706': BigInteger; + '707': BigInteger; + '708': BigInteger; + '709': BigInteger; + '710': BigInteger; + '711': BigInteger; + '712': BigInteger; + '713': BigInteger; + '714': BigInteger; + '715': BigInteger; + '716': BigInteger; + '717': BigInteger; + '718': BigInteger; + '719': BigInteger; + '720': BigInteger; + '721': BigInteger; + '722': BigInteger; + '723': BigInteger; + '724': BigInteger; + '725': BigInteger; + '726': BigInteger; + '727': BigInteger; + '728': BigInteger; + '729': BigInteger; + '730': BigInteger; + '731': BigInteger; + '732': BigInteger; + '733': BigInteger; + '734': BigInteger; + '735': BigInteger; + '736': BigInteger; + '737': BigInteger; + '738': BigInteger; + '739': BigInteger; + '740': BigInteger; + '741': BigInteger; + '742': BigInteger; + '743': BigInteger; + '744': BigInteger; + '745': BigInteger; + '746': BigInteger; + '747': BigInteger; + '748': BigInteger; + '749': BigInteger; + '750': BigInteger; + '751': BigInteger; + '752': BigInteger; + '753': BigInteger; + '754': BigInteger; + '755': BigInteger; + '756': BigInteger; + '757': BigInteger; + '758': BigInteger; + '759': BigInteger; + '760': BigInteger; + '761': BigInteger; + '762': BigInteger; + '763': BigInteger; + '764': BigInteger; + '765': BigInteger; + '766': BigInteger; + '767': BigInteger; + '768': BigInteger; + '769': BigInteger; + '770': BigInteger; + '771': BigInteger; + '772': BigInteger; + '773': BigInteger; + '774': BigInteger; + '775': BigInteger; + '776': BigInteger; + '777': BigInteger; + '778': BigInteger; + '779': BigInteger; + '780': BigInteger; + '781': BigInteger; + '782': BigInteger; + '783': BigInteger; + '784': BigInteger; + '785': BigInteger; + '786': BigInteger; + '787': BigInteger; + '788': BigInteger; + '789': BigInteger; + '790': BigInteger; + '791': BigInteger; + '792': BigInteger; + '793': BigInteger; + '794': BigInteger; + '795': BigInteger; + '796': BigInteger; + '797': BigInteger; + '798': BigInteger; + '799': BigInteger; + '800': BigInteger; + '801': BigInteger; + '802': BigInteger; + '803': BigInteger; + '804': BigInteger; + '805': BigInteger; + '806': BigInteger; + '807': BigInteger; + '808': BigInteger; + '809': BigInteger; + '810': BigInteger; + '811': BigInteger; + '812': BigInteger; + '813': BigInteger; + '814': BigInteger; + '815': BigInteger; + '816': BigInteger; + '817': BigInteger; + '818': BigInteger; + '819': BigInteger; + '820': BigInteger; + '821': BigInteger; + '822': BigInteger; + '823': BigInteger; + '824': BigInteger; + '825': BigInteger; + '826': BigInteger; + '827': BigInteger; + '828': BigInteger; + '829': BigInteger; + '830': BigInteger; + '831': BigInteger; + '832': BigInteger; + '833': BigInteger; + '834': BigInteger; + '835': BigInteger; + '836': BigInteger; + '837': BigInteger; + '838': BigInteger; + '839': BigInteger; + '840': BigInteger; + '841': BigInteger; + '842': BigInteger; + '843': BigInteger; + '844': BigInteger; + '845': BigInteger; + '846': BigInteger; + '847': BigInteger; + '848': BigInteger; + '849': BigInteger; + '850': BigInteger; + '851': BigInteger; + '852': BigInteger; + '853': BigInteger; + '854': BigInteger; + '855': BigInteger; + '856': BigInteger; + '857': BigInteger; + '858': BigInteger; + '859': BigInteger; + '860': BigInteger; + '861': BigInteger; + '862': BigInteger; + '863': BigInteger; + '864': BigInteger; + '865': BigInteger; + '866': BigInteger; + '867': BigInteger; + '868': BigInteger; + '869': BigInteger; + '870': BigInteger; + '871': BigInteger; + '872': BigInteger; + '873': BigInteger; + '874': BigInteger; + '875': BigInteger; + '876': BigInteger; + '877': BigInteger; + '878': BigInteger; + '879': BigInteger; + '880': BigInteger; + '881': BigInteger; + '882': BigInteger; + '883': BigInteger; + '884': BigInteger; + '885': BigInteger; + '886': BigInteger; + '887': BigInteger; + '888': BigInteger; + '889': BigInteger; + '890': BigInteger; + '891': BigInteger; + '892': BigInteger; + '893': BigInteger; + '894': BigInteger; + '895': BigInteger; + '896': BigInteger; + '897': BigInteger; + '898': BigInteger; + '899': BigInteger; + '900': BigInteger; + '901': BigInteger; + '902': BigInteger; + '903': BigInteger; + '904': BigInteger; + '905': BigInteger; + '906': BigInteger; + '907': BigInteger; + '908': BigInteger; + '909': BigInteger; + '910': BigInteger; + '911': BigInteger; + '912': BigInteger; + '913': BigInteger; + '914': BigInteger; + '915': BigInteger; + '916': BigInteger; + '917': BigInteger; + '918': BigInteger; + '919': BigInteger; + '920': BigInteger; + '921': BigInteger; + '922': BigInteger; + '923': BigInteger; + '924': BigInteger; + '925': BigInteger; + '926': BigInteger; + '927': BigInteger; + '928': BigInteger; + '929': BigInteger; + '930': BigInteger; + '931': BigInteger; + '932': BigInteger; + '933': BigInteger; + '934': BigInteger; + '935': BigInteger; + '936': BigInteger; + '937': BigInteger; + '938': BigInteger; + '939': BigInteger; + '940': BigInteger; + '941': BigInteger; + '942': BigInteger; + '943': BigInteger; + '944': BigInteger; + '945': BigInteger; + '946': BigInteger; + '947': BigInteger; + '948': BigInteger; + '949': BigInteger; + '950': BigInteger; + '951': BigInteger; + '952': BigInteger; + '953': BigInteger; + '954': BigInteger; + '955': BigInteger; + '956': BigInteger; + '957': BigInteger; + '958': BigInteger; + '959': BigInteger; + '960': BigInteger; + '961': BigInteger; + '962': BigInteger; + '963': BigInteger; + '964': BigInteger; + '965': BigInteger; + '966': BigInteger; + '967': BigInteger; + '968': BigInteger; + '969': BigInteger; + '970': BigInteger; + '971': BigInteger; + '972': BigInteger; + '973': BigInteger; + '974': BigInteger; + '975': BigInteger; + '976': BigInteger; + '977': BigInteger; + '978': BigInteger; + '979': BigInteger; + '980': BigInteger; + '981': BigInteger; + '982': BigInteger; + '983': BigInteger; + '984': BigInteger; + '985': BigInteger; + '986': BigInteger; + '987': BigInteger; + '988': BigInteger; + '989': BigInteger; + '990': BigInteger; + '991': BigInteger; + '992': BigInteger; + '993': BigInteger; + '994': BigInteger; + '995': BigInteger; + '996': BigInteger; + '997': BigInteger; + '998': BigInteger; + '999': BigInteger; + } + + interface BaseArray { + value: number[], + isNegative: boolean + } +} diff --git a/node_modules/big-integer/BigInteger.js b/node_modules/big-integer/BigInteger.js new file mode 100644 index 0000000..e8658dc --- /dev/null +++ b/node_modules/big-integer/BigInteger.js @@ -0,0 +1,1453 @@ +var bigInt = (function (undefined) { + "use strict"; + + var BASE = 1e7, + LOG_BASE = 7, + MAX_INT = 9007199254740992, + MAX_INT_ARR = smallToArray(MAX_INT), + DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"; + + var supportsNativeBigInt = typeof BigInt === "function"; + + function Integer(v, radix, alphabet, caseSensitive) { + if (typeof v === "undefined") return Integer[0]; + if (typeof radix !== "undefined") return +radix === 10 && !alphabet ? parseValue(v) : parseBase(v, radix, alphabet, caseSensitive); + return parseValue(v); + } + + function BigInteger(value, sign) { + this.value = value; + this.sign = sign; + this.isSmall = false; + } + BigInteger.prototype = Object.create(Integer.prototype); + + function SmallInteger(value) { + this.value = value; + this.sign = value < 0; + this.isSmall = true; + } + SmallInteger.prototype = Object.create(Integer.prototype); + + function NativeBigInt(value) { + this.value = value; + } + NativeBigInt.prototype = Object.create(Integer.prototype); + + function isPrecise(n) { + return -MAX_INT < n && n < MAX_INT; + } + + function smallToArray(n) { // For performance reasons doesn't reference BASE, need to change this function if BASE changes + if (n < 1e7) + return [n]; + if (n < 1e14) + return [n % 1e7, Math.floor(n / 1e7)]; + return [n % 1e7, Math.floor(n / 1e7) % 1e7, Math.floor(n / 1e14)]; + } + + function arrayToSmall(arr) { // If BASE changes this function may need to change + trim(arr); + var length = arr.length; + if (length < 4 && compareAbs(arr, MAX_INT_ARR) < 0) { + switch (length) { + case 0: return 0; + case 1: return arr[0]; + case 2: return arr[0] + arr[1] * BASE; + default: return arr[0] + (arr[1] + arr[2] * BASE) * BASE; + } + } + return arr; + } + + function trim(v) { + var i = v.length; + while (v[--i] === 0); + v.length = i + 1; + } + + function createArray(length) { // function shamelessly stolen from Yaffle's library https://github.com/Yaffle/BigInteger + var x = new Array(length); + var i = -1; + while (++i < length) { + x[i] = 0; + } + return x; + } + + function truncate(n) { + if (n > 0) return Math.floor(n); + return Math.ceil(n); + } + + function add(a, b) { // assumes a and b are arrays with a.length >= b.length + var l_a = a.length, + l_b = b.length, + r = new Array(l_a), + carry = 0, + base = BASE, + sum, i; + for (i = 0; i < l_b; i++) { + sum = a[i] + b[i] + carry; + carry = sum >= base ? 1 : 0; + r[i] = sum - carry * base; + } + while (i < l_a) { + sum = a[i] + carry; + carry = sum === base ? 1 : 0; + r[i++] = sum - carry * base; + } + if (carry > 0) r.push(carry); + return r; + } + + function addAny(a, b) { + if (a.length >= b.length) return add(a, b); + return add(b, a); + } + + function addSmall(a, carry) { // assumes a is array, carry is number with 0 <= carry < MAX_INT + var l = a.length, + r = new Array(l), + base = BASE, + sum, i; + for (i = 0; i < l; i++) { + sum = a[i] - base + carry; + carry = Math.floor(sum / base); + r[i] = sum - carry * base; + carry += 1; + } + while (carry > 0) { + r[i++] = carry % base; + carry = Math.floor(carry / base); + } + return r; + } + + BigInteger.prototype.add = function (v) { + var n = parseValue(v); + if (this.sign !== n.sign) { + return this.subtract(n.negate()); + } + var a = this.value, b = n.value; + if (n.isSmall) { + return new BigInteger(addSmall(a, Math.abs(b)), this.sign); + } + return new BigInteger(addAny(a, b), this.sign); + }; + BigInteger.prototype.plus = BigInteger.prototype.add; + + SmallInteger.prototype.add = function (v) { + var n = parseValue(v); + var a = this.value; + if (a < 0 !== n.sign) { + return this.subtract(n.negate()); + } + var b = n.value; + if (n.isSmall) { + if (isPrecise(a + b)) return new SmallInteger(a + b); + b = smallToArray(Math.abs(b)); + } + return new BigInteger(addSmall(b, Math.abs(a)), a < 0); + }; + SmallInteger.prototype.plus = SmallInteger.prototype.add; + + NativeBigInt.prototype.add = function (v) { + return new NativeBigInt(this.value + parseValue(v).value); + } + NativeBigInt.prototype.plus = NativeBigInt.prototype.add; + + function subtract(a, b) { // assumes a and b are arrays with a >= b + var a_l = a.length, + b_l = b.length, + r = new Array(a_l), + borrow = 0, + base = BASE, + i, difference; + for (i = 0; i < b_l; i++) { + difference = a[i] - borrow - b[i]; + if (difference < 0) { + difference += base; + borrow = 1; + } else borrow = 0; + r[i] = difference; + } + for (i = b_l; i < a_l; i++) { + difference = a[i] - borrow; + if (difference < 0) difference += base; + else { + r[i++] = difference; + break; + } + r[i] = difference; + } + for (; i < a_l; i++) { + r[i] = a[i]; + } + trim(r); + return r; + } + + function subtractAny(a, b, sign) { + var value; + if (compareAbs(a, b) >= 0) { + value = subtract(a, b); + } else { + value = subtract(b, a); + sign = !sign; + } + value = arrayToSmall(value); + if (typeof value === "number") { + if (sign) value = -value; + return new SmallInteger(value); + } + return new BigInteger(value, sign); + } + + function subtractSmall(a, b, sign) { // assumes a is array, b is number with 0 <= b < MAX_INT + var l = a.length, + r = new Array(l), + carry = -b, + base = BASE, + i, difference; + for (i = 0; i < l; i++) { + difference = a[i] + carry; + carry = Math.floor(difference / base); + difference %= base; + r[i] = difference < 0 ? difference + base : difference; + } + r = arrayToSmall(r); + if (typeof r === "number") { + if (sign) r = -r; + return new SmallInteger(r); + } return new BigInteger(r, sign); + } + + BigInteger.prototype.subtract = function (v) { + var n = parseValue(v); + if (this.sign !== n.sign) { + return this.add(n.negate()); + } + var a = this.value, b = n.value; + if (n.isSmall) + return subtractSmall(a, Math.abs(b), this.sign); + return subtractAny(a, b, this.sign); + }; + BigInteger.prototype.minus = BigInteger.prototype.subtract; + + SmallInteger.prototype.subtract = function (v) { + var n = parseValue(v); + var a = this.value; + if (a < 0 !== n.sign) { + return this.add(n.negate()); + } + var b = n.value; + if (n.isSmall) { + return new SmallInteger(a - b); + } + return subtractSmall(b, Math.abs(a), a >= 0); + }; + SmallInteger.prototype.minus = SmallInteger.prototype.subtract; + + NativeBigInt.prototype.subtract = function (v) { + return new NativeBigInt(this.value - parseValue(v).value); + } + NativeBigInt.prototype.minus = NativeBigInt.prototype.subtract; + + BigInteger.prototype.negate = function () { + return new BigInteger(this.value, !this.sign); + }; + SmallInteger.prototype.negate = function () { + var sign = this.sign; + var small = new SmallInteger(-this.value); + small.sign = !sign; + return small; + }; + NativeBigInt.prototype.negate = function () { + return new NativeBigInt(-this.value); + } + + BigInteger.prototype.abs = function () { + return new BigInteger(this.value, false); + }; + SmallInteger.prototype.abs = function () { + return new SmallInteger(Math.abs(this.value)); + }; + NativeBigInt.prototype.abs = function () { + return new NativeBigInt(this.value >= 0 ? this.value : -this.value); + } + + + function multiplyLong(a, b) { + var a_l = a.length, + b_l = b.length, + l = a_l + b_l, + r = createArray(l), + base = BASE, + product, carry, i, a_i, b_j; + for (i = 0; i < a_l; ++i) { + a_i = a[i]; + for (var j = 0; j < b_l; ++j) { + b_j = b[j]; + product = a_i * b_j + r[i + j]; + carry = Math.floor(product / base); + r[i + j] = product - carry * base; + r[i + j + 1] += carry; + } + } + trim(r); + return r; + } + + function multiplySmall(a, b) { // assumes a is array, b is number with |b| < BASE + var l = a.length, + r = new Array(l), + base = BASE, + carry = 0, + product, i; + for (i = 0; i < l; i++) { + product = a[i] * b + carry; + carry = Math.floor(product / base); + r[i] = product - carry * base; + } + while (carry > 0) { + r[i++] = carry % base; + carry = Math.floor(carry / base); + } + return r; + } + + function shiftLeft(x, n) { + var r = []; + while (n-- > 0) r.push(0); + return r.concat(x); + } + + function multiplyKaratsuba(x, y) { + var n = Math.max(x.length, y.length); + + if (n <= 30) return multiplyLong(x, y); + n = Math.ceil(n / 2); + + var b = x.slice(n), + a = x.slice(0, n), + d = y.slice(n), + c = y.slice(0, n); + + var ac = multiplyKaratsuba(a, c), + bd = multiplyKaratsuba(b, d), + abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d)); + + var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), bd), n)), shiftLeft(bd, 2 * n)); + trim(product); + return product; + } + + // The following function is derived from a surface fit of a graph plotting the performance difference + // between long multiplication and karatsuba multiplication versus the lengths of the two arrays. + function useKaratsuba(l1, l2) { + return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0; + } + + BigInteger.prototype.multiply = function (v) { + var n = parseValue(v), + a = this.value, b = n.value, + sign = this.sign !== n.sign, + abs; + if (n.isSmall) { + if (b === 0) return Integer[0]; + if (b === 1) return this; + if (b === -1) return this.negate(); + abs = Math.abs(b); + if (abs < BASE) { + return new BigInteger(multiplySmall(a, abs), sign); + } + b = smallToArray(abs); + } + if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for certain array sizes + return new BigInteger(multiplyKaratsuba(a, b), sign); + return new BigInteger(multiplyLong(a, b), sign); + }; + + BigInteger.prototype.times = BigInteger.prototype.multiply; + + function multiplySmallAndArray(a, b, sign) { // a >= 0 + if (a < BASE) { + return new BigInteger(multiplySmall(b, a), sign); + } + return new BigInteger(multiplyLong(b, smallToArray(a)), sign); + } + SmallInteger.prototype._multiplyBySmall = function (a) { + if (isPrecise(a.value * this.value)) { + return new SmallInteger(a.value * this.value); + } + return multiplySmallAndArray(Math.abs(a.value), smallToArray(Math.abs(this.value)), this.sign !== a.sign); + }; + BigInteger.prototype._multiplyBySmall = function (a) { + if (a.value === 0) return Integer[0]; + if (a.value === 1) return this; + if (a.value === -1) return this.negate(); + return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign !== a.sign); + }; + SmallInteger.prototype.multiply = function (v) { + return parseValue(v)._multiplyBySmall(this); + }; + SmallInteger.prototype.times = SmallInteger.prototype.multiply; + + NativeBigInt.prototype.multiply = function (v) { + return new NativeBigInt(this.value * parseValue(v).value); + } + NativeBigInt.prototype.times = NativeBigInt.prototype.multiply; + + function square(a) { + //console.assert(2 * BASE * BASE < MAX_INT); + var l = a.length, + r = createArray(l + l), + base = BASE, + product, carry, i, a_i, a_j; + for (i = 0; i < l; i++) { + a_i = a[i]; + carry = 0 - a_i * a_i; + for (var j = i; j < l; j++) { + a_j = a[j]; + product = 2 * (a_i * a_j) + r[i + j] + carry; + carry = Math.floor(product / base); + r[i + j] = product - carry * base; + } + r[i + l] = carry; + } + trim(r); + return r; + } + + BigInteger.prototype.square = function () { + return new BigInteger(square(this.value), false); + }; + + SmallInteger.prototype.square = function () { + var value = this.value * this.value; + if (isPrecise(value)) return new SmallInteger(value); + return new BigInteger(square(smallToArray(Math.abs(this.value))), false); + }; + + NativeBigInt.prototype.square = function (v) { + return new NativeBigInt(this.value * this.value); + } + + function divMod1(a, b) { // Left over from previous version. Performs faster than divMod2 on smaller input sizes. + var a_l = a.length, + b_l = b.length, + base = BASE, + result = createArray(b.length), + divisorMostSignificantDigit = b[b_l - 1], + // normalization + lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)), + remainder = multiplySmall(a, lambda), + divisor = multiplySmall(b, lambda), + quotientDigit, shift, carry, borrow, i, l, q; + if (remainder.length <= a_l) remainder.push(0); + divisor.push(0); + divisorMostSignificantDigit = divisor[b_l - 1]; + for (shift = a_l - b_l; shift >= 0; shift--) { + quotientDigit = base - 1; + if (remainder[shift + b_l] !== divisorMostSignificantDigit) { + quotientDigit = Math.floor((remainder[shift + b_l] * base + remainder[shift + b_l - 1]) / divisorMostSignificantDigit); + } + // quotientDigit <= base - 1 + carry = 0; + borrow = 0; + l = divisor.length; + for (i = 0; i < l; i++) { + carry += quotientDigit * divisor[i]; + q = Math.floor(carry / base); + borrow += remainder[shift + i] - (carry - q * base); + carry = q; + if (borrow < 0) { + remainder[shift + i] = borrow + base; + borrow = -1; + } else { + remainder[shift + i] = borrow; + borrow = 0; + } + } + while (borrow !== 0) { + quotientDigit -= 1; + carry = 0; + for (i = 0; i < l; i++) { + carry += remainder[shift + i] - base + divisor[i]; + if (carry < 0) { + remainder[shift + i] = carry + base; + carry = 0; + } else { + remainder[shift + i] = carry; + carry = 1; + } + } + borrow += carry; + } + result[shift] = quotientDigit; + } + // denormalization + remainder = divModSmall(remainder, lambda)[0]; + return [arrayToSmall(result), arrayToSmall(remainder)]; + } + + function divMod2(a, b) { // Implementation idea shamelessly stolen from Silent Matt's library http://silentmatt.com/biginteger/ + // Performs faster than divMod1 on larger input sizes. + var a_l = a.length, + b_l = b.length, + result = [], + part = [], + base = BASE, + guess, xlen, highx, highy, check; + while (a_l) { + part.unshift(a[--a_l]); + trim(part); + if (compareAbs(part, b) < 0) { + result.push(0); + continue; + } + xlen = part.length; + highx = part[xlen - 1] * base + part[xlen - 2]; + highy = b[b_l - 1] * base + b[b_l - 2]; + if (xlen > b_l) { + highx = (highx + 1) * base; + } + guess = Math.ceil(highx / highy); + do { + check = multiplySmall(b, guess); + if (compareAbs(check, part) <= 0) break; + guess--; + } while (guess); + result.push(guess); + part = subtract(part, check); + } + result.reverse(); + return [arrayToSmall(result), arrayToSmall(part)]; + } + + function divModSmall(value, lambda) { + var length = value.length, + quotient = createArray(length), + base = BASE, + i, q, remainder, divisor; + remainder = 0; + for (i = length - 1; i >= 0; --i) { + divisor = remainder * base + value[i]; + q = truncate(divisor / lambda); + remainder = divisor - q * lambda; + quotient[i] = q | 0; + } + return [quotient, remainder | 0]; + } + + function divModAny(self, v) { + var value, n = parseValue(v); + if (supportsNativeBigInt) { + return [new NativeBigInt(self.value / n.value), new NativeBigInt(self.value % n.value)]; + } + var a = self.value, b = n.value; + var quotient; + if (b === 0) throw new Error("Cannot divide by zero"); + if (self.isSmall) { + if (n.isSmall) { + return [new SmallInteger(truncate(a / b)), new SmallInteger(a % b)]; + } + return [Integer[0], self]; + } + if (n.isSmall) { + if (b === 1) return [self, Integer[0]]; + if (b == -1) return [self.negate(), Integer[0]]; + var abs = Math.abs(b); + if (abs < BASE) { + value = divModSmall(a, abs); + quotient = arrayToSmall(value[0]); + var remainder = value[1]; + if (self.sign) remainder = -remainder; + if (typeof quotient === "number") { + if (self.sign !== n.sign) quotient = -quotient; + return [new SmallInteger(quotient), new SmallInteger(remainder)]; + } + return [new BigInteger(quotient, self.sign !== n.sign), new SmallInteger(remainder)]; + } + b = smallToArray(abs); + } + var comparison = compareAbs(a, b); + if (comparison === -1) return [Integer[0], self]; + if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], Integer[0]]; + + // divMod1 is faster on smaller input sizes + if (a.length + b.length <= 200) + value = divMod1(a, b); + else value = divMod2(a, b); + + quotient = value[0]; + var qSign = self.sign !== n.sign, + mod = value[1], + mSign = self.sign; + if (typeof quotient === "number") { + if (qSign) quotient = -quotient; + quotient = new SmallInteger(quotient); + } else quotient = new BigInteger(quotient, qSign); + if (typeof mod === "number") { + if (mSign) mod = -mod; + mod = new SmallInteger(mod); + } else mod = new BigInteger(mod, mSign); + return [quotient, mod]; + } + + BigInteger.prototype.divmod = function (v) { + var result = divModAny(this, v); + return { + quotient: result[0], + remainder: result[1] + }; + }; + NativeBigInt.prototype.divmod = SmallInteger.prototype.divmod = BigInteger.prototype.divmod; + + + BigInteger.prototype.divide = function (v) { + return divModAny(this, v)[0]; + }; + NativeBigInt.prototype.over = NativeBigInt.prototype.divide = function (v) { + return new NativeBigInt(this.value / parseValue(v).value); + }; + SmallInteger.prototype.over = SmallInteger.prototype.divide = BigInteger.prototype.over = BigInteger.prototype.divide; + + BigInteger.prototype.mod = function (v) { + return divModAny(this, v)[1]; + }; + NativeBigInt.prototype.mod = NativeBigInt.prototype.remainder = function (v) { + return new NativeBigInt(this.value % parseValue(v).value); + }; + SmallInteger.prototype.remainder = SmallInteger.prototype.mod = BigInteger.prototype.remainder = BigInteger.prototype.mod; + + BigInteger.prototype.pow = function (v) { + var n = parseValue(v), + a = this.value, + b = n.value, + value, x, y; + if (b === 0) return Integer[1]; + if (a === 0) return Integer[0]; + if (a === 1) return Integer[1]; + if (a === -1) return n.isEven() ? Integer[1] : Integer[-1]; + if (n.sign) { + return Integer[0]; + } + if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is too large."); + if (this.isSmall) { + if (isPrecise(value = Math.pow(a, b))) + return new SmallInteger(truncate(value)); + } + x = this; + y = Integer[1]; + while (true) { + if (b & 1 === 1) { + y = y.times(x); + --b; + } + if (b === 0) break; + b /= 2; + x = x.square(); + } + return y; + }; + SmallInteger.prototype.pow = BigInteger.prototype.pow; + + NativeBigInt.prototype.pow = function (v) { + var n = parseValue(v); + var a = this.value, b = n.value; + var _0 = BigInt(0), _1 = BigInt(1), _2 = BigInt(2); + if (b === _0) return Integer[1]; + if (a === _0) return Integer[0]; + if (a === _1) return Integer[1]; + if (a === BigInt(-1)) return n.isEven() ? Integer[1] : Integer[-1]; + if (n.isNegative()) return new NativeBigInt(_0); + var x = this; + var y = Integer[1]; + while (true) { + if ((b & _1) === _1) { + y = y.times(x); + --b; + } + if (b === _0) break; + b /= _2; + x = x.square(); + } + return y; + } + + BigInteger.prototype.modPow = function (exp, mod) { + exp = parseValue(exp); + mod = parseValue(mod); + if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0"); + var r = Integer[1], + base = this.mod(mod); + if (exp.isNegative()) { + exp = exp.multiply(Integer[-1]); + base = base.modInv(mod); + } + while (exp.isPositive()) { + if (base.isZero()) return Integer[0]; + if (exp.isOdd()) r = r.multiply(base).mod(mod); + exp = exp.divide(2); + base = base.square().mod(mod); + } + return r; + }; + NativeBigInt.prototype.modPow = SmallInteger.prototype.modPow = BigInteger.prototype.modPow; + + function compareAbs(a, b) { + if (a.length !== b.length) { + return a.length > b.length ? 1 : -1; + } + for (var i = a.length - 1; i >= 0; i--) { + if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1; + } + return 0; + } + + BigInteger.prototype.compareAbs = function (v) { + var n = parseValue(v), + a = this.value, + b = n.value; + if (n.isSmall) return 1; + return compareAbs(a, b); + }; + SmallInteger.prototype.compareAbs = function (v) { + var n = parseValue(v), + a = Math.abs(this.value), + b = n.value; + if (n.isSmall) { + b = Math.abs(b); + return a === b ? 0 : a > b ? 1 : -1; + } + return -1; + }; + NativeBigInt.prototype.compareAbs = function (v) { + var a = this.value; + var b = parseValue(v).value; + a = a >= 0 ? a : -a; + b = b >= 0 ? b : -b; + return a === b ? 0 : a > b ? 1 : -1; + } + + BigInteger.prototype.compare = function (v) { + // See discussion about comparison with Infinity: + // https://github.com/peterolson/BigInteger.js/issues/61 + if (v === Infinity) { + return -1; + } + if (v === -Infinity) { + return 1; + } + + var n = parseValue(v), + a = this.value, + b = n.value; + if (this.sign !== n.sign) { + return n.sign ? 1 : -1; + } + if (n.isSmall) { + return this.sign ? -1 : 1; + } + return compareAbs(a, b) * (this.sign ? -1 : 1); + }; + BigInteger.prototype.compareTo = BigInteger.prototype.compare; + + SmallInteger.prototype.compare = function (v) { + if (v === Infinity) { + return -1; + } + if (v === -Infinity) { + return 1; + } + + var n = parseValue(v), + a = this.value, + b = n.value; + if (n.isSmall) { + return a == b ? 0 : a > b ? 1 : -1; + } + if (a < 0 !== n.sign) { + return a < 0 ? -1 : 1; + } + return a < 0 ? 1 : -1; + }; + SmallInteger.prototype.compareTo = SmallInteger.prototype.compare; + + NativeBigInt.prototype.compare = function (v) { + if (v === Infinity) { + return -1; + } + if (v === -Infinity) { + return 1; + } + var a = this.value; + var b = parseValue(v).value; + return a === b ? 0 : a > b ? 1 : -1; + } + NativeBigInt.prototype.compareTo = NativeBigInt.prototype.compare; + + BigInteger.prototype.equals = function (v) { + return this.compare(v) === 0; + }; + NativeBigInt.prototype.eq = NativeBigInt.prototype.equals = SmallInteger.prototype.eq = SmallInteger.prototype.equals = BigInteger.prototype.eq = BigInteger.prototype.equals; + + BigInteger.prototype.notEquals = function (v) { + return this.compare(v) !== 0; + }; + NativeBigInt.prototype.neq = NativeBigInt.prototype.notEquals = SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = BigInteger.prototype.neq = BigInteger.prototype.notEquals; + + BigInteger.prototype.greater = function (v) { + return this.compare(v) > 0; + }; + NativeBigInt.prototype.gt = NativeBigInt.prototype.greater = SmallInteger.prototype.gt = SmallInteger.prototype.greater = BigInteger.prototype.gt = BigInteger.prototype.greater; + + BigInteger.prototype.lesser = function (v) { + return this.compare(v) < 0; + }; + NativeBigInt.prototype.lt = NativeBigInt.prototype.lesser = SmallInteger.prototype.lt = SmallInteger.prototype.lesser = BigInteger.prototype.lt = BigInteger.prototype.lesser; + + BigInteger.prototype.greaterOrEquals = function (v) { + return this.compare(v) >= 0; + }; + NativeBigInt.prototype.geq = NativeBigInt.prototype.greaterOrEquals = SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals; + + BigInteger.prototype.lesserOrEquals = function (v) { + return this.compare(v) <= 0; + }; + NativeBigInt.prototype.leq = NativeBigInt.prototype.lesserOrEquals = SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals; + + BigInteger.prototype.isEven = function () { + return (this.value[0] & 1) === 0; + }; + SmallInteger.prototype.isEven = function () { + return (this.value & 1) === 0; + }; + NativeBigInt.prototype.isEven = function () { + return (this.value & BigInt(1)) === BigInt(0); + } + + BigInteger.prototype.isOdd = function () { + return (this.value[0] & 1) === 1; + }; + SmallInteger.prototype.isOdd = function () { + return (this.value & 1) === 1; + }; + NativeBigInt.prototype.isOdd = function () { + return (this.value & BigInt(1)) === BigInt(1); + } + + BigInteger.prototype.isPositive = function () { + return !this.sign; + }; + SmallInteger.prototype.isPositive = function () { + return this.value > 0; + }; + NativeBigInt.prototype.isPositive = SmallInteger.prototype.isPositive; + + BigInteger.prototype.isNegative = function () { + return this.sign; + }; + SmallInteger.prototype.isNegative = function () { + return this.value < 0; + }; + NativeBigInt.prototype.isNegative = SmallInteger.prototype.isNegative; + + BigInteger.prototype.isUnit = function () { + return false; + }; + SmallInteger.prototype.isUnit = function () { + return Math.abs(this.value) === 1; + }; + NativeBigInt.prototype.isUnit = function () { + return this.abs().value === BigInt(1); + } + + BigInteger.prototype.isZero = function () { + return false; + }; + SmallInteger.prototype.isZero = function () { + return this.value === 0; + }; + NativeBigInt.prototype.isZero = function () { + return this.value === BigInt(0); + } + + BigInteger.prototype.isDivisibleBy = function (v) { + var n = parseValue(v); + if (n.isZero()) return false; + if (n.isUnit()) return true; + if (n.compareAbs(2) === 0) return this.isEven(); + return this.mod(n).isZero(); + }; + NativeBigInt.prototype.isDivisibleBy = SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy; + + function isBasicPrime(v) { + var n = v.abs(); + if (n.isUnit()) return false; + if (n.equals(2) || n.equals(3) || n.equals(5)) return true; + if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false; + if (n.lesser(49)) return true; + // we don't know if it's prime: let the other functions figure it out + } + + function millerRabinTest(n, a) { + var nPrev = n.prev(), + b = nPrev, + r = 0, + d, t, i, x; + while (b.isEven()) b = b.divide(2), r++; + next: for (i = 0; i < a.length; i++) { + if (n.lesser(a[i])) continue; + x = bigInt(a[i]).modPow(b, n); + if (x.isUnit() || x.equals(nPrev)) continue; + for (d = r - 1; d != 0; d--) { + x = x.square().mod(n); + if (x.isUnit()) return false; + if (x.equals(nPrev)) continue next; + } + return false; + } + return true; + } + + // Set "strict" to true to force GRH-supported lower bound of 2*log(N)^2 + BigInteger.prototype.isPrime = function (strict) { + var isPrime = isBasicPrime(this); + if (isPrime !== undefined) return isPrime; + var n = this.abs(); + var bits = n.bitLength(); + if (bits <= 64) + return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]); + var logN = Math.log(2) * bits.toJSNumber(); + var t = Math.ceil((strict === true) ? (2 * Math.pow(logN, 2)) : logN); + for (var a = [], i = 0; i < t; i++) { + a.push(bigInt(i + 2)); + } + return millerRabinTest(n, a); + }; + NativeBigInt.prototype.isPrime = SmallInteger.prototype.isPrime = BigInteger.prototype.isPrime; + + BigInteger.prototype.isProbablePrime = function (iterations, rng) { + var isPrime = isBasicPrime(this); + if (isPrime !== undefined) return isPrime; + var n = this.abs(); + var t = iterations === undefined ? 5 : iterations; + for (var a = [], i = 0; i < t; i++) { + a.push(bigInt.randBetween(2, n.minus(2), rng)); + } + return millerRabinTest(n, a); + }; + NativeBigInt.prototype.isProbablePrime = SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime; + + BigInteger.prototype.modInv = function (n) { + var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR; + while (!newR.isZero()) { + q = r.divide(newR); + lastT = t; + lastR = r; + t = newT; + r = newR; + newT = lastT.subtract(q.multiply(newT)); + newR = lastR.subtract(q.multiply(newR)); + } + if (!r.isUnit()) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime"); + if (t.compare(0) === -1) { + t = t.add(n); + } + if (this.isNegative()) { + return t.negate(); + } + return t; + }; + + NativeBigInt.prototype.modInv = SmallInteger.prototype.modInv = BigInteger.prototype.modInv; + + BigInteger.prototype.next = function () { + var value = this.value; + if (this.sign) { + return subtractSmall(value, 1, this.sign); + } + return new BigInteger(addSmall(value, 1), this.sign); + }; + SmallInteger.prototype.next = function () { + var value = this.value; + if (value + 1 < MAX_INT) return new SmallInteger(value + 1); + return new BigInteger(MAX_INT_ARR, false); + }; + NativeBigInt.prototype.next = function () { + return new NativeBigInt(this.value + BigInt(1)); + } + + BigInteger.prototype.prev = function () { + var value = this.value; + if (this.sign) { + return new BigInteger(addSmall(value, 1), true); + } + return subtractSmall(value, 1, this.sign); + }; + SmallInteger.prototype.prev = function () { + var value = this.value; + if (value - 1 > -MAX_INT) return new SmallInteger(value - 1); + return new BigInteger(MAX_INT_ARR, true); + }; + NativeBigInt.prototype.prev = function () { + return new NativeBigInt(this.value - BigInt(1)); + } + + var powersOfTwo = [1]; + while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]); + var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1]; + + function shift_isSmall(n) { + return Math.abs(n) <= BASE; + } + + BigInteger.prototype.shiftLeft = function (v) { + var n = parseValue(v).toJSNumber(); + if (!shift_isSmall(n)) { + throw new Error(String(n) + " is too large for shifting."); + } + if (n < 0) return this.shiftRight(-n); + var result = this; + if (result.isZero()) return result; + while (n >= powers2Length) { + result = result.multiply(highestPower2); + n -= powers2Length - 1; + } + return result.multiply(powersOfTwo[n]); + }; + NativeBigInt.prototype.shiftLeft = SmallInteger.prototype.shiftLeft = BigInteger.prototype.shiftLeft; + + BigInteger.prototype.shiftRight = function (v) { + var remQuo; + var n = parseValue(v).toJSNumber(); + if (!shift_isSmall(n)) { + throw new Error(String(n) + " is too large for shifting."); + } + if (n < 0) return this.shiftLeft(-n); + var result = this; + while (n >= powers2Length) { + if (result.isZero() || (result.isNegative() && result.isUnit())) return result; + remQuo = divModAny(result, highestPower2); + result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; + n -= powers2Length - 1; + } + remQuo = divModAny(result, powersOfTwo[n]); + return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; + }; + NativeBigInt.prototype.shiftRight = SmallInteger.prototype.shiftRight = BigInteger.prototype.shiftRight; + + function bitwise(x, y, fn) { + y = parseValue(y); + var xSign = x.isNegative(), ySign = y.isNegative(); + var xRem = xSign ? x.not() : x, + yRem = ySign ? y.not() : y; + var xDigit = 0, yDigit = 0; + var xDivMod = null, yDivMod = null; + var result = []; + while (!xRem.isZero() || !yRem.isZero()) { + xDivMod = divModAny(xRem, highestPower2); + xDigit = xDivMod[1].toJSNumber(); + if (xSign) { + xDigit = highestPower2 - 1 - xDigit; // two's complement for negative numbers + } + + yDivMod = divModAny(yRem, highestPower2); + yDigit = yDivMod[1].toJSNumber(); + if (ySign) { + yDigit = highestPower2 - 1 - yDigit; // two's complement for negative numbers + } + + xRem = xDivMod[0]; + yRem = yDivMod[0]; + result.push(fn(xDigit, yDigit)); + } + var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : bigInt(0); + for (var i = result.length - 1; i >= 0; i -= 1) { + sum = sum.multiply(highestPower2).add(bigInt(result[i])); + } + return sum; + } + + BigInteger.prototype.not = function () { + return this.negate().prev(); + }; + NativeBigInt.prototype.not = SmallInteger.prototype.not = BigInteger.prototype.not; + + BigInteger.prototype.and = function (n) { + return bitwise(this, n, function (a, b) { return a & b; }); + }; + NativeBigInt.prototype.and = SmallInteger.prototype.and = BigInteger.prototype.and; + + BigInteger.prototype.or = function (n) { + return bitwise(this, n, function (a, b) { return a | b; }); + }; + NativeBigInt.prototype.or = SmallInteger.prototype.or = BigInteger.prototype.or; + + BigInteger.prototype.xor = function (n) { + return bitwise(this, n, function (a, b) { return a ^ b; }); + }; + NativeBigInt.prototype.xor = SmallInteger.prototype.xor = BigInteger.prototype.xor; + + var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | LOBMASK_I; + function roughLOB(n) { // get lowestOneBit (rough) + // SmallInteger: return Min(lowestOneBit(n), 1 << 30) + // BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7] + var v = n.value, + x = typeof v === "number" ? v | LOBMASK_I : + typeof v === "bigint" ? v | BigInt(LOBMASK_I) : + v[0] + v[1] * BASE | LOBMASK_BI; + return x & -x; + } + + function integerLogarithm(value, base) { + if (base.compareTo(value) <= 0) { + var tmp = integerLogarithm(value, base.square(base)); + var p = tmp.p; + var e = tmp.e; + var t = p.multiply(base); + return t.compareTo(value) <= 0 ? { p: t, e: e * 2 + 1 } : { p: p, e: e * 2 }; + } + return { p: bigInt(1), e: 0 }; + } + + BigInteger.prototype.bitLength = function () { + var n = this; + if (n.compareTo(bigInt(0)) < 0) { + n = n.negate().subtract(bigInt(1)); + } + if (n.compareTo(bigInt(0)) === 0) { + return bigInt(0); + } + return bigInt(integerLogarithm(n, bigInt(2)).e).add(bigInt(1)); + } + NativeBigInt.prototype.bitLength = SmallInteger.prototype.bitLength = BigInteger.prototype.bitLength; + + function max(a, b) { + a = parseValue(a); + b = parseValue(b); + return a.greater(b) ? a : b; + } + function min(a, b) { + a = parseValue(a); + b = parseValue(b); + return a.lesser(b) ? a : b; + } + function gcd(a, b) { + a = parseValue(a).abs(); + b = parseValue(b).abs(); + if (a.equals(b)) return a; + if (a.isZero()) return b; + if (b.isZero()) return a; + var c = Integer[1], d, t; + while (a.isEven() && b.isEven()) { + d = min(roughLOB(a), roughLOB(b)); + a = a.divide(d); + b = b.divide(d); + c = c.multiply(d); + } + while (a.isEven()) { + a = a.divide(roughLOB(a)); + } + do { + while (b.isEven()) { + b = b.divide(roughLOB(b)); + } + if (a.greater(b)) { + t = b; b = a; a = t; + } + b = b.subtract(a); + } while (!b.isZero()); + return c.isUnit() ? a : a.multiply(c); + } + function lcm(a, b) { + a = parseValue(a).abs(); + b = parseValue(b).abs(); + return a.divide(gcd(a, b)).multiply(b); + } + function randBetween(a, b, rng) { + a = parseValue(a); + b = parseValue(b); + var usedRNG = rng || Math.random; + var low = min(a, b), high = max(a, b); + var range = high.subtract(low).add(1); + if (range.isSmall) return low.add(Math.floor(usedRNG() * range)); + var digits = toBase(range, BASE).value; + var result = [], restricted = true; + for (var i = 0; i < digits.length; i++) { + var top = restricted ? digits[i] + (i + 1 < digits.length ? digits[i + 1] / BASE : 0) : BASE; + var digit = truncate(usedRNG() * top); + result.push(digit); + if (digit < digits[i]) restricted = false; + } + return low.add(Integer.fromArray(result, BASE, false)); + } + + var parseBase = function (text, base, alphabet, caseSensitive) { + alphabet = alphabet || DEFAULT_ALPHABET; + text = String(text); + if (!caseSensitive) { + text = text.toLowerCase(); + alphabet = alphabet.toLowerCase(); + } + var length = text.length; + var i; + var absBase = Math.abs(base); + var alphabetValues = {}; + for (i = 0; i < alphabet.length; i++) { + alphabetValues[alphabet[i]] = i; + } + for (i = 0; i < length; i++) { + var c = text[i]; + if (c === "-") continue; + if (c in alphabetValues) { + if (alphabetValues[c] >= absBase) { + if (c === "1" && absBase === 1) continue; + throw new Error(c + " is not a valid digit in base " + base + "."); + } + } + } + base = parseValue(base); + var digits = []; + var isNegative = text[0] === "-"; + for (i = isNegative ? 1 : 0; i < text.length; i++) { + var c = text[i]; + if (c in alphabetValues) digits.push(parseValue(alphabetValues[c])); + else if (c === "<") { + var start = i; + do { i++; } while (text[i] !== ">" && i < text.length); + digits.push(parseValue(text.slice(start + 1, i))); + } + else throw new Error(c + " is not a valid character"); + } + return parseBaseFromArray(digits, base, isNegative); + }; + + function parseBaseFromArray(digits, base, isNegative) { + var val = Integer[0], pow = Integer[1], i; + for (i = digits.length - 1; i >= 0; i--) { + val = val.add(digits[i].times(pow)); + pow = pow.times(base); + } + return isNegative ? val.negate() : val; + } + + function stringify(digit, alphabet) { + alphabet = alphabet || DEFAULT_ALPHABET; + if (digit < alphabet.length) { + return alphabet[digit]; + } + return "<" + digit + ">"; + } + + function toBase(n, base) { + base = bigInt(base); + if (base.isZero()) { + if (n.isZero()) return { value: [0], isNegative: false }; + throw new Error("Cannot convert nonzero numbers to base 0."); + } + if (base.equals(-1)) { + if (n.isZero()) return { value: [0], isNegative: false }; + if (n.isNegative()) + return { + value: [].concat.apply([], Array.apply(null, Array(-n.toJSNumber())) + .map(Array.prototype.valueOf, [1, 0]) + ), + isNegative: false + }; + + var arr = Array.apply(null, Array(n.toJSNumber() - 1)) + .map(Array.prototype.valueOf, [0, 1]); + arr.unshift([1]); + return { + value: [].concat.apply([], arr), + isNegative: false + }; + } + + var neg = false; + if (n.isNegative() && base.isPositive()) { + neg = true; + n = n.abs(); + } + if (base.isUnit()) { + if (n.isZero()) return { value: [0], isNegative: false }; + + return { + value: Array.apply(null, Array(n.toJSNumber())) + .map(Number.prototype.valueOf, 1), + isNegative: neg + }; + } + var out = []; + var left = n, divmod; + while (left.isNegative() || left.compareAbs(base) >= 0) { + divmod = left.divmod(base); + left = divmod.quotient; + var digit = divmod.remainder; + if (digit.isNegative()) { + digit = base.minus(digit).abs(); + left = left.next(); + } + out.push(digit.toJSNumber()); + } + out.push(left.toJSNumber()); + return { value: out.reverse(), isNegative: neg }; + } + + function toBaseString(n, base, alphabet) { + var arr = toBase(n, base); + return (arr.isNegative ? "-" : "") + arr.value.map(function (x) { + return stringify(x, alphabet); + }).join(''); + } + + BigInteger.prototype.toArray = function (radix) { + return toBase(this, radix); + }; + + SmallInteger.prototype.toArray = function (radix) { + return toBase(this, radix); + }; + + NativeBigInt.prototype.toArray = function (radix) { + return toBase(this, radix); + }; + + BigInteger.prototype.toString = function (radix, alphabet) { + if (radix === undefined) radix = 10; + if (radix !== 10 || alphabet) return toBaseString(this, radix, alphabet); + var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit; + while (--l >= 0) { + digit = String(v[l]); + str += zeros.slice(digit.length) + digit; + } + var sign = this.sign ? "-" : ""; + return sign + str; + }; + + SmallInteger.prototype.toString = function (radix, alphabet) { + if (radix === undefined) radix = 10; + if (radix != 10 || alphabet) return toBaseString(this, radix, alphabet); + return String(this.value); + }; + + NativeBigInt.prototype.toString = SmallInteger.prototype.toString; + + NativeBigInt.prototype.toJSON = BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function () { return this.toString(); } + + BigInteger.prototype.valueOf = function () { + return parseInt(this.toString(), 10); + }; + BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf; + + SmallInteger.prototype.valueOf = function () { + return this.value; + }; + SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf; + NativeBigInt.prototype.valueOf = NativeBigInt.prototype.toJSNumber = function () { + return parseInt(this.toString(), 10); + } + + function parseStringValue(v) { + if (isPrecise(+v)) { + var x = +v; + if (x === truncate(x)) + return supportsNativeBigInt ? new NativeBigInt(BigInt(x)) : new SmallInteger(x); + throw new Error("Invalid integer: " + v); + } + var sign = v[0] === "-"; + if (sign) v = v.slice(1); + var split = v.split(/e/i); + if (split.length > 2) throw new Error("Invalid integer: " + split.join("e")); + if (split.length === 2) { + var exp = split[1]; + if (exp[0] === "+") exp = exp.slice(1); + exp = +exp; + if (exp !== truncate(exp) || !isPrecise(exp)) throw new Error("Invalid integer: " + exp + " is not a valid exponent."); + var text = split[0]; + var decimalPlace = text.indexOf("."); + if (decimalPlace >= 0) { + exp -= text.length - decimalPlace - 1; + text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 1); + } + if (exp < 0) throw new Error("Cannot include negative exponent part for integers"); + text += (new Array(exp + 1)).join("0"); + v = text; + } + var isValid = /^([0-9][0-9]*)$/.test(v); + if (!isValid) throw new Error("Invalid integer: " + v); + if (supportsNativeBigInt) { + return new NativeBigInt(BigInt(sign ? "-" + v : v)); + } + var r = [], max = v.length, l = LOG_BASE, min = max - l; + while (max > 0) { + r.push(+v.slice(min, max)); + min -= l; + if (min < 0) min = 0; + max -= l; + } + trim(r); + return new BigInteger(r, sign); + } + + function parseNumberValue(v) { + if (supportsNativeBigInt) { + return new NativeBigInt(BigInt(v)); + } + if (isPrecise(v)) { + if (v !== truncate(v)) throw new Error(v + " is not an integer."); + return new SmallInteger(v); + } + return parseStringValue(v.toString()); + } + + function parseValue(v) { + if (typeof v === "number") { + return parseNumberValue(v); + } + if (typeof v === "string") { + return parseStringValue(v); + } + if (typeof v === "bigint") { + return new NativeBigInt(v); + } + return v; + } + // Pre-define numbers in range [-999,999] + for (var i = 0; i < 1000; i++) { + Integer[i] = parseValue(i); + if (i > 0) Integer[-i] = parseValue(-i); + } + // Backwards compatibility + Integer.one = Integer[1]; + Integer.zero = Integer[0]; + Integer.minusOne = Integer[-1]; + Integer.max = max; + Integer.min = min; + Integer.gcd = gcd; + Integer.lcm = lcm; + Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger || x instanceof NativeBigInt; }; + Integer.randBetween = randBetween; + + Integer.fromArray = function (digits, base, isNegative) { + return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative); + }; + + return Integer; +})(); + +// Node.js check +if (typeof module !== "undefined" && module.hasOwnProperty("exports")) { + module.exports = bigInt; +} + +//amd check +if (typeof define === "function" && define.amd) { + define( function () { + return bigInt; + }); +} diff --git a/node_modules/big-integer/BigInteger.min.js b/node_modules/big-integer/BigInteger.min.js new file mode 100644 index 0000000..a960fa6 --- /dev/null +++ b/node_modules/big-integer/BigInteger.min.js @@ -0,0 +1 @@ +var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),DEFAULT_ALPHABET="0123456789abcdefghijklmnopqrstuvwxyz";var supportsNativeBigInt=typeof BigInt==="function";function Integer(v,radix,alphabet,caseSensitive){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10&&!alphabet?parseValue(v):parseBase(v,radix,alphabet,caseSensitive);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function NativeBigInt(value){this.value=value}NativeBigInt.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;NativeBigInt.prototype.add=function(v){return new NativeBigInt(this.value+parseValue(v).value)};NativeBigInt.prototype.plus=NativeBigInt.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;NativeBigInt.prototype.subtract=function(v){return new NativeBigInt(this.value-parseValue(v).value)};NativeBigInt.prototype.minus=NativeBigInt.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};NativeBigInt.prototype.negate=function(){return new NativeBigInt(-this.value)};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};NativeBigInt.prototype.abs=function(){return new NativeBigInt(this.value>=0?this.value:-this.value)};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);if(supportsNativeBigInt){return[new NativeBigInt(self.value/n.value),new NativeBigInt(self.value%n.value)]}var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};NativeBigInt.prototype.compareAbs=function(v){var a=this.value;var b=parseValue(v).value;a=a>=0?a:-a;b=b>=0?b:-b;return a===b?0:a>b?1:-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;NativeBigInt.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var a=this.value;var b=parseValue(v).value;return a===b?0:a>b?1:-1};NativeBigInt.prototype.compareTo=NativeBigInt.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};NativeBigInt.prototype.eq=NativeBigInt.prototype.equals=SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};NativeBigInt.prototype.neq=NativeBigInt.prototype.notEquals=SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};NativeBigInt.prototype.gt=NativeBigInt.prototype.greater=SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};NativeBigInt.prototype.lt=NativeBigInt.prototype.lesser=SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};NativeBigInt.prototype.geq=NativeBigInt.prototype.greaterOrEquals=SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};NativeBigInt.prototype.leq=NativeBigInt.prototype.lesserOrEquals=SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};NativeBigInt.prototype.isEven=function(){return(this.value&BigInt(1))===BigInt(0)};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};NativeBigInt.prototype.isOdd=function(){return(this.value&BigInt(1))===BigInt(1)};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};NativeBigInt.prototype.isPositive=SmallInteger.prototype.isPositive;BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};NativeBigInt.prototype.isNegative=SmallInteger.prototype.isNegative;BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};NativeBigInt.prototype.isUnit=function(){return this.abs().value===BigInt(1)};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};NativeBigInt.prototype.isZero=function(){return this.value===BigInt(0)};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);if(n.isZero())return false;if(n.isUnit())return true;if(n.compareAbs(2)===0)return this.isEven();return this.mod(n).isZero()};NativeBigInt.prototype.isDivisibleBy=SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(49))return true}function millerRabinTest(n,a){var nPrev=n.prev(),b=nPrev,r=0,d,t,i,x;while(b.isEven())b=b.divide(2),r++;next:for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};NativeBigInt.prototype.prev=function(){return new NativeBigInt(this.value-BigInt(1))};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return Math.abs(n)<=BASE}BigInteger.prototype.shiftLeft=function(v){var n=parseValue(v).toJSNumber();if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}if(n<0)return this.shiftRight(-n);var result=this;if(result.isZero())return result;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};NativeBigInt.prototype.shiftLeft=SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(v){var remQuo;var n=parseValue(v).toJSNumber();if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero()||result.isNegative()&&result.isUnit())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};NativeBigInt.prototype.shiftRight=SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};NativeBigInt.prototype.not=SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};NativeBigInt.prototype.and=SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};NativeBigInt.prototype.or=SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};NativeBigInt.prototype.xor=SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:typeof v==="bigint"?v|BigInt(LOBMASK_I):v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function integerLogarithm(value,base){if(base.compareTo(value)<=0){var tmp=integerLogarithm(value,base.square(base));var p=tmp.p;var e=tmp.e;var t=p.multiply(base);return t.compareTo(value)<=0?{p:t,e:e*2+1}:{p:p,e:e*2}}return{p:bigInt(1),e:0}}BigInteger.prototype.bitLength=function(){var n=this;if(n.compareTo(bigInt(0))<0){n=n.negate().subtract(bigInt(1))}if(n.compareTo(bigInt(0))===0){return bigInt(0)}return bigInt(integerLogarithm(n,bigInt(2)).e).add(bigInt(1))};NativeBigInt.prototype.bitLength=SmallInteger.prototype.bitLength=BigInteger.prototype.bitLength;function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b,rng){a=parseValue(a);b=parseValue(b);var usedRNG=rng||Math.random;var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(usedRNG()*range));var digits=toBase(range,BASE).value;var result=[],restricted=true;for(var i=0;i=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i"&&i=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit,alphabet){alphabet=alphabet||DEFAULT_ALPHABET;if(digit"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return{value:[0],isNegative:false};throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return{value:[0],isNegative:false};if(n.isNegative())return{value:[].concat.apply([],Array.apply(null,Array(-n.toJSNumber())).map(Array.prototype.valueOf,[1,0])),isNegative:false};var arr=Array.apply(null,Array(n.toJSNumber()-1)).map(Array.prototype.valueOf,[0,1]);arr.unshift([1]);return{value:[].concat.apply([],arr),isNegative:false}}var neg=false;if(n.isNegative()&&base.isPositive()){neg=true;n=n.abs()}if(base.isUnit()){if(n.isZero())return{value:[0],isNegative:false};return{value:Array.apply(null,Array(n.toJSNumber())).map(Number.prototype.valueOf,1),isNegative:neg}}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(digit.toJSNumber())}out.push(left.toJSNumber());return{value:out.reverse(),isNegative:neg}}function toBaseString(n,base,alphabet){var arr=toBase(n,base);return(arr.isNegative?"-":"")+arr.value.map(function(x){return stringify(x,alphabet)}).join("")}BigInteger.prototype.toArray=function(radix){return toBase(this,radix)};SmallInteger.prototype.toArray=function(radix){return toBase(this,radix)};NativeBigInt.prototype.toArray=function(radix){return toBase(this,radix)};BigInteger.prototype.toString=function(radix,alphabet){if(radix===undefined)radix=10;if(radix!==10||alphabet)return toBaseString(this,radix,alphabet);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix,alphabet){if(radix===undefined)radix=10;if(radix!=10||alphabet)return toBaseString(this,radix,alphabet);return String(this.value)};NativeBigInt.prototype.toString=SmallInteger.prototype.toString;NativeBigInt.prototype.toJSON=BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return parseInt(this.toString(),10)};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;NativeBigInt.prototype.valueOf=NativeBigInt.prototype.toJSNumber=function(){return parseInt(this.toString(),10)};function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return supportsNativeBigInt?new NativeBigInt(BigInt(x)):new SmallInteger(x);throw new Error("Invalid integer: "+v)}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);if(supportsNativeBigInt){return new NativeBigInt(BigInt(sign?"-"+v:v))}var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(supportsNativeBigInt){return new NativeBigInt(BigInt(v))}if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}if(typeof v==="bigint"){return new NativeBigInt(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=parseValue(i);if(i>0)Integer[-i]=parseValue(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger||x instanceof NativeBigInt};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define(function(){return bigInt})} \ No newline at end of file diff --git a/node_modules/big-integer/LICENSE b/node_modules/big-integer/LICENSE new file mode 100644 index 0000000..cf1ab25 --- /dev/null +++ b/node_modules/big-integer/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/node_modules/big-integer/README.md b/node_modules/big-integer/README.md new file mode 100644 index 0000000..d72420f --- /dev/null +++ b/node_modules/big-integer/README.md @@ -0,0 +1,589 @@ +# BigInteger.js [![Build Status][travis-img]][travis-url] [![Coverage Status][coveralls-img]][coveralls-url] [![Monthly Downloads][downloads-img]][downloads-url] + +[travis-url]: https://travis-ci.org/peterolson/BigInteger.js +[travis-img]: https://travis-ci.org/peterolson/BigInteger.js.svg?branch=master +[coveralls-url]: https://coveralls.io/github/peterolson/BigInteger.js?branch=master +[coveralls-img]: https://coveralls.io/repos/peterolson/BigInteger.js/badge.svg?branch=master&service=github +[downloads-url]: https://www.npmjs.com/package/big-integer +[downloads-img]: https://img.shields.io/npm/dm/big-integer.svg + +**BigInteger.js** is an arbitrary-length integer library for Javascript, allowing arithmetic operations on integers of unlimited size, notwithstanding memory and time limitations. + +**Update (December 2, 2018):** [`BigInt` is being added as a native feature of JavaScript](https://tc39.github.io/proposal-bigint/). This library now works as a polyfill: if the environment supports the native `BigInt`, this library acts as a thin wrapper over the native implementation. + +## Installation + +If you are using a browser, you can download [BigInteger.js from GitHub](http://peterolson.github.com/BigInteger.js/BigInteger.min.js) or just hotlink to it: + + + +If you are using node, you can install BigInteger with [npm](https://npmjs.org/). + + npm install big-integer + +Then you can include it in your code: + + var bigInt = require("big-integer"); + + +## Usage +### `bigInt(number, [base], [alphabet], [caseSensitive])` + +You can create a bigInt by calling the `bigInt` function. You can pass in + + - a string, which it will parse as an bigInt and throw an `"Invalid integer"` error if the parsing fails. + - a Javascript number, which it will parse as an bigInt and throw an `"Invalid integer"` error if the parsing fails. + - another bigInt. + - nothing, and it will return `bigInt.zero`. + + If you provide a second parameter, then it will parse `number` as a number in base `base`. Note that `base` can be any bigInt (even negative or zero). The letters "a-z" and "A-Z" will be interpreted as the numbers 10 to 35. Higher digits can be specified in angle brackets (`<` and `>`). The default `base` is `10`. + + You can specify a custom alphabet for base conversion with the third parameter. The default `alphabet` is `"0123456789abcdefghijklmnopqrstuvwxyz"`. + + The fourth parameter specifies whether or not the number string should be case-sensitive, i.e. whether `a` and `A` should be treated as different digits. By default `caseSensitive` is `false`. + +Examples: + + var zero = bigInt(); + var ninetyThree = bigInt(93); + var largeNumber = bigInt("75643564363473453456342378564387956906736546456235345"); + var googol = bigInt("1e100"); + var bigNumber = bigInt(largeNumber); + + var maximumByte = bigInt("FF", 16); + var fiftyFiveGoogol = bigInt("<55>0", googol); + +Note that Javascript numbers larger than `9007199254740992` and smaller than `-9007199254740992` are not precisely represented numbers and will not produce exact results. If you are dealing with numbers outside that range, it is better to pass in strings. + +### Method Chaining + +Note that bigInt operations return bigInts, which allows you to chain methods, for example: + + var salary = bigInt(dollarsPerHour).times(hoursWorked).plus(randomBonuses) + +### Constants + +There are three named constants already stored that you do not have to construct with the `bigInt` function yourself: + + - `bigInt.one`, equivalent to `bigInt(1)` + - `bigInt.zero`, equivalent to `bigInt(0)` + - `bigInt.minusOne`, equivalent to `bigInt(-1)` + +The numbers from -999 to 999 are also already prestored and can be accessed using `bigInt[index]`, for example: + + - `bigInt[-999]`, equivalent to `bigInt(-999)` + - `bigInt[256]`, equivalent to `bigInt(256)` + +### Methods + +#### `abs()` + +Returns the absolute value of a bigInt. + + - `bigInt(-45).abs()` => `45` + - `bigInt(45).abs()` => `45` + +#### `add(number)` + +Performs addition. + + - `bigInt(5).add(7)` => `12` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Addition) + +#### `and(number)` + +Performs the bitwise AND operation. The operands are treated as if they were represented using [two's complement representation](http://en.wikipedia.org/wiki/Two%27s_complement). + + - `bigInt(6).and(3)` => `2` + - `bigInt(6).and(-3)` => `4` + +#### `bitLength()` + +Returns the number of digits required to represent a bigInt in binary. + + - `bigInt(5)` => `3` (since 5 is `101` in binary, which is three digits long) + +#### `compare(number)` + +Performs a comparison between two numbers. If the numbers are equal, it returns `0`. If the first number is greater, it returns `1`. If the first number is lesser, it returns `-1`. + + - `bigInt(5).compare(5)` => `0` + - `bigInt(5).compare(4)` => `1` + - `bigInt(4).compare(5)` => `-1` + +#### `compareAbs(number)` + +Performs a comparison between the absolute value of two numbers. + + - `bigInt(5).compareAbs(-5)` => `0` + - `bigInt(5).compareAbs(4)` => `1` + - `bigInt(4).compareAbs(-5)` => `-1` + +#### `compareTo(number)` + +Alias for the `compare` method. + +#### `divide(number)` + +Performs integer division, disregarding the remainder. + + - `bigInt(59).divide(5)` => `11` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Division) + +#### `divmod(number)` + +Performs division and returns an object with two properties: `quotient` and `remainder`. The sign of the remainder will match the sign of the dividend. + + - `bigInt(59).divmod(5)` => `{quotient: bigInt(11), remainder: bigInt(4) }` + - `bigInt(-5).divmod(2)` => `{quotient: bigInt(-2), remainder: bigInt(-1) }` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Division) + +#### `eq(number)` + +Alias for the `equals` method. + +#### `equals(number)` + +Checks if two numbers are equal. + + - `bigInt(5).equals(5)` => `true` + - `bigInt(4).equals(7)` => `false` + +#### `geq(number)` + +Alias for the `greaterOrEquals` method. + + +#### `greater(number)` + +Checks if the first number is greater than the second. + + - `bigInt(5).greater(6)` => `false` + - `bigInt(5).greater(5)` => `false` + - `bigInt(5).greater(4)` => `true` + +#### `greaterOrEquals(number)` + +Checks if the first number is greater than or equal to the second. + + - `bigInt(5).greaterOrEquals(6)` => `false` + - `bigInt(5).greaterOrEquals(5)` => `true` + - `bigInt(5).greaterOrEquals(4)` => `true` + +#### `gt(number)` + +Alias for the `greater` method. + +#### `isDivisibleBy(number)` + +Returns `true` if the first number is divisible by the second number, `false` otherwise. + + - `bigInt(999).isDivisibleBy(333)` => `true` + - `bigInt(99).isDivisibleBy(5)` => `false` + +#### `isEven()` + +Returns `true` if the number is even, `false` otherwise. + + - `bigInt(6).isEven()` => `true` + - `bigInt(3).isEven()` => `false` + +#### `isNegative()` + +Returns `true` if the number is negative, `false` otherwise. +Returns `false` for `0` and `-0`. + + - `bigInt(-23).isNegative()` => `true` + - `bigInt(50).isNegative()` => `false` + +#### `isOdd()` + +Returns `true` if the number is odd, `false` otherwise. + + - `bigInt(13).isOdd()` => `true` + - `bigInt(40).isOdd()` => `false` + +#### `isPositive()` + +Return `true` if the number is positive, `false` otherwise. +Returns `false` for `0` and `-0`. + + - `bigInt(54).isPositive()` => `true` + - `bigInt(-1).isPositive()` => `false` + +#### `isPrime(strict?)` + +Returns `true` if the number is prime, `false` otherwise. +Set "strict" boolean to true to force GRH-supported lower bound of 2*log(N)^2. + + - `bigInt(5).isPrime()` => `true` + - `bigInt(6).isPrime()` => `false` + +#### `isProbablePrime([iterations], [rng])` + +Returns `true` if the number is very likely to be prime, `false` otherwise. +Supplying `iterations` is optional - it determines the number of iterations of the test (default: `5`). The more iterations, the lower chance of getting a false positive. +This uses the [Miller Rabin test](https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test). + + - `bigInt(5).isProbablePrime()` => `true` + - `bigInt(49).isProbablePrime()` => `false` + - `bigInt(1729).isProbablePrime()` => `false` + +Note that this function is not deterministic, since it relies on random sampling of factors, so the result for some numbers is not always the same - unless you pass a predictable random number generator as `rng`. The behavior and requirements are the same as with `randBetween`. + + - `bigInt(1729).isProbablePrime(1, () => 0.1)` => `false` + - `bigInt(1729).isProbablePrime(1, () => 0.2)` => `true` + +If the number is composite then the Miller–Rabin primality test declares the number probably prime with a probability at most `4` to the power `−iterations`. +If the number is prime, this function always returns `true`. + +#### `isUnit()` + +Returns `true` if the number is `1` or `-1`, `false` otherwise. + + - `bigInt.one.isUnit()` => `true` + - `bigInt.minusOne.isUnit()` => `true` + - `bigInt(5).isUnit()` => `false` + +#### `isZero()` + +Return `true` if the number is `0` or `-0`, `false` otherwise. + + - `bigInt.zero.isZero()` => `true` + - `bigInt("-0").isZero()` => `true` + - `bigInt(50).isZero()` => `false` + +#### `leq(number)` + +Alias for the `lesserOrEquals` method. + +#### `lesser(number)` + +Checks if the first number is lesser than the second. + + - `bigInt(5).lesser(6)` => `true` + - `bigInt(5).lesser(5)` => `false` + - `bigInt(5).lesser(4)` => `false` + +#### `lesserOrEquals(number)` + +Checks if the first number is less than or equal to the second. + + - `bigInt(5).lesserOrEquals(6)` => `true` + - `bigInt(5).lesserOrEquals(5)` => `true` + - `bigInt(5).lesserOrEquals(4)` => `false` + +#### `lt(number)` + +Alias for the `lesser` method. + +#### `minus(number)` + +Alias for the `subtract` method. + + - `bigInt(3).minus(5)` => `-2` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Subtraction) + +#### `mod(number)` + +Performs division and returns the remainder, disregarding the quotient. The sign of the remainder will match the sign of the dividend. + + - `bigInt(59).mod(5)` => `4` + - `bigInt(-5).mod(2)` => `-1` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Division) + +#### `modInv(mod)` + +Finds the [multiplicative inverse](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse) of the number modulo `mod`. + + - `bigInt(3).modInv(11)` => `4` + - `bigInt(42).modInv(2017)` => `1969` + +#### `modPow(exp, mod)` + +Takes the number to the power `exp` modulo `mod`. + + - `bigInt(10).modPow(3, 30)` => `10` + +#### `multiply(number)` + +Performs multiplication. + + - `bigInt(111).multiply(111)` => `12321` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Multiplication) + +#### `neq(number)` + +Alias for the `notEquals` method. + +#### `next()` + +Adds one to the number. + + - `bigInt(6).next()` => `7` + +#### `not()` + +Performs the bitwise NOT operation. The operands are treated as if they were represented using [two's complement representation](http://en.wikipedia.org/wiki/Two%27s_complement). + + - `bigInt(10).not()` => `-11` + - `bigInt(0).not()` => `-1` + +#### `notEquals(number)` + +Checks if two numbers are not equal. + + - `bigInt(5).notEquals(5)` => `false` + - `bigInt(4).notEquals(7)` => `true` + +#### `or(number)` + +Performs the bitwise OR operation. The operands are treated as if they were represented using [two's complement representation](http://en.wikipedia.org/wiki/Two%27s_complement). + + - `bigInt(13).or(10)` => `15` + - `bigInt(13).or(-8)` => `-3` + +#### `over(number)` + +Alias for the `divide` method. + + - `bigInt(59).over(5)` => `11` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Division) + +#### `plus(number)` + +Alias for the `add` method. + + - `bigInt(5).plus(7)` => `12` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Addition) + +#### `pow(number)` + +Performs exponentiation. If the exponent is less than `0`, `pow` returns `0`. `bigInt.zero.pow(0)` returns `1`. + + - `bigInt(16).pow(16)` => `18446744073709551616` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Exponentiation) + +#### `prev(number)` + +Subtracts one from the number. + + - `bigInt(6).prev()` => `5` + +#### `remainder(number)` + +Alias for the `mod` method. + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Division) + +#### `shiftLeft(n)` + +Shifts the number left by `n` places in its binary representation. If a negative number is provided, it will shift right. Throws an error if `n` is outside of the range `[-9007199254740992, 9007199254740992]`. + + - `bigInt(8).shiftLeft(2)` => `32` + - `bigInt(8).shiftLeft(-2)` => `2` + +#### `shiftRight(n)` + +Shifts the number right by `n` places in its binary representation. If a negative number is provided, it will shift left. Throws an error if `n` is outside of the range `[-9007199254740992, 9007199254740992]`. + + - `bigInt(8).shiftRight(2)` => `2` + - `bigInt(8).shiftRight(-2)` => `32` + +#### `square()` + +Squares the number + + - `bigInt(3).square()` => `9` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Squaring) + +#### `subtract(number)` + +Performs subtraction. + + - `bigInt(3).subtract(5)` => `-2` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Subtraction) + +#### `times(number)` + +Alias for the `multiply` method. + + - `bigInt(111).times(111)` => `12321` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#Multiplication) + +#### `toArray(radix)` + +Converts a bigInt into an object with the properties "value" and "isNegative." "Value" is an array of integers modulo the given radix. "isNegative" is a boolean that represents the sign of the result. + + - `bigInt("1e9").toArray(10)` => { + value: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], + isNegative: false + } + - `bigInt("1e9").toArray(16)` => { + value: [3, 11, 9, 10, 12, 10, 0, 0], + isNegative: false + } + - `bigInt(567890).toArray(100)` => { + value: [56, 78, 90], + isNegative: false + } + +Negative bases are supported. + + - `bigInt(12345).toArray(-10)` => { + value: [2, 8, 4, 6, 5], + isNegative: false + } + +Base 1 and base -1 are also supported. + + - `bigInt(-15).toArray(1)` => { + value: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + isNegative: true + } + - `bigInt(-15).toArray(-1)` => { + value: [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], + isNegative: false + } + +Base 0 is only allowed for the number zero. + + - `bigInt(0).toArray(0)` => { + value: [0], + isNegative: false + } + - `bigInt(1).toArray(0)` => `Error: Cannot convert nonzero numbers to base 0.` + +#### `toJSNumber()` + +Converts a bigInt into a native Javascript number. Loses precision for numbers outside the range `[-9007199254740992, 9007199254740992]`. + + - `bigInt("18446744073709551616").toJSNumber()` => `18446744073709552000` + +#### `xor(number)` + +Performs the bitwise XOR operation. The operands are treated as if they were represented using [two's complement representation](http://en.wikipedia.org/wiki/Two%27s_complement). + + - `bigInt(12).xor(5)` => `9` + - `bigInt(12).xor(-5)` => `-9` + +### Static Methods + +#### `fromArray(digits, base = 10, isNegative?)` + +Constructs a bigInt from an array of digits in base `base`. The optional `isNegative` flag will make the number negative. + + - `bigInt.fromArray([1, 2, 3, 4, 5], 10)` => `12345` + - `bigInt.fromArray([1, 0, 0], 2, true)` => `-4` + +#### `gcd(a, b)` + +Finds the greatest common denominator of `a` and `b`. + + - `bigInt.gcd(42,56)` => `14` + +#### `isInstance(x)` + +Returns `true` if `x` is a BigInteger, `false` otherwise. + + - `bigInt.isInstance(bigInt(14))` => `true` + - `bigInt.isInstance(14)` => `false` + +#### `lcm(a,b)` + +Finds the least common multiple of `a` and `b`. + + - `bigInt.lcm(21, 6)` => `42` + +#### `max(a,b)` + +Returns the largest of `a` and `b`. + + - `bigInt.max(77, 432)` => `432` + +#### `min(a,b)` + +Returns the smallest of `a` and `b`. + + - `bigInt.min(77, 432)` => `77` + +#### `randBetween(min, max, [rng])` + +Returns a random number between `min` and `max`, optionally using `rng` to generate randomness. + + - `bigInt.randBetween("-1e100", "1e100")` => (for example) `8494907165436643479673097939554427056789510374838494147955756275846226209006506706784609314471378745` + +`rng` should take no arguments and return a `number` between 0 and 1. It defaults to `Math.random`. + + - `bigInt.randBetween("-1e100", "1e100", () => 0.5)` => (always) `50000005000000500000050000005000000500000050000005000000500000050000005000000500000050000005000000` + + +### Override Methods + +#### `toString(radix = 10, [alphabet])` + +Converts a bigInt to a string. There is an optional radix parameter (which defaults to 10) that converts the number to the given radix. Digits in the range `10-35` will use the letters `a-z`. + + - `bigInt("1e9").toString()` => `"1000000000"` + - `bigInt("1e9").toString(16)` => `"3b9aca00"` + + You can use a custom base alphabet with the second parameter. The default `alphabet` is `"0123456789abcdefghijklmnopqrstuvwxyz"`. + + - `bigInt("5").toString(2, "aA")` => `"AaA"` + +**Note that arithmetical operators will trigger the `valueOf` function rather than the `toString` function.** When converting a bigInteger to a string, you should use the `toString` method or the `String` function instead of adding the empty string. + + - `bigInt("999999999999999999").toString()` => `"999999999999999999"` + - `String(bigInt("999999999999999999"))` => `"999999999999999999"` + - `bigInt("999999999999999999") + ""` => `1000000000000000000` + +Bases larger than 36 are supported. If a digit is greater than or equal to 36, it will be enclosed in angle brackets. + + - `bigInt(567890).toString(100)` => `"<56><78><90>"` + +Negative bases are also supported. + + - `bigInt(12345).toString(-10)` => `"28465"` + +Base 1 and base -1 are also supported. + + - `bigInt(-15).toString(1)` => `"-111111111111111"` + - `bigInt(-15).toString(-1)` => `"101010101010101010101010101010"` + +Base 0 is only allowed for the number zero. + + - `bigInt(0).toString(0)` => `0` + - `bigInt(1).toString(0)` => `Error: Cannot convert nonzero numbers to base 0.` + +[View benchmarks for this method](http://peterolson.github.io/BigInteger.js/benchmark/#toString) + +#### `valueOf()` + +Converts a bigInt to a native Javascript number. This override allows you to use native arithmetic operators without explicit conversion: + + - `bigInt("100") + bigInt("200") === 300; //true` + +## Contributors + +To contribute, just fork the project, make some changes, and submit a pull request. Please verify that the unit tests pass before submitting. + +The unit tests are contained in the `spec/spec.js` file. You can run them locally by opening the `spec/SpecRunner.html` or file or running `npm test`. You can also [run the tests online from GitHub](http://peterolson.github.io/BigInteger.js/spec/SpecRunner.html). + +There are performance benchmarks that can be viewed from the `benchmarks/index.html` page. You can [run them online from GitHub](http://peterolson.github.io/BigInteger.js/benchmark/). + +## License + +This project is public domain. For more details, read about the [Unlicense](http://unlicense.org/). diff --git a/node_modules/big-integer/bower.json b/node_modules/big-integer/bower.json new file mode 100644 index 0000000..22dc58f --- /dev/null +++ b/node_modules/big-integer/bower.json @@ -0,0 +1,29 @@ +{ + "name": "big-integer", + "description": "An arbitrary length integer library for Javascript", + "main": "./BigInteger.js", + "authors": [ + "Peter Olson" + ], + "license": "Unlicense", + "keywords": [ + "math", + "big", + "bignum", + "bigint", + "biginteger", + "integer", + "arbitrary", + "precision", + "arithmetic" + ], + "homepage": "https://github.com/peterolson/BigInteger.js", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "coverage", + "tests" + ] +} diff --git a/node_modules/big-integer/package.json b/node_modules/big-integer/package.json new file mode 100644 index 0000000..742570a --- /dev/null +++ b/node_modules/big-integer/package.json @@ -0,0 +1,48 @@ +{ + "name": "big-integer", + "version": "1.6.52", + "author": "Peter Olson ", + "description": "An arbitrary length integer library for Javascript", + "contributors": [], + "bin": {}, + "scripts": { + "test": "tsc && karma start my.conf.js && node spec/tsDefinitions.js", + "minify": "uglifyjs BigInteger.js -o BigInteger.min.js" + }, + "main": "./BigInteger", + "repository": { + "type": "git", + "url": "git@github.com:peterolson/BigInteger.js.git" + }, + "keywords": [ + "math", + "big", + "bignum", + "bigint", + "biginteger", + "integer", + "arbitrary", + "precision", + "arithmetic" + ], + "devDependencies": { + "@types/lodash": "^4.14.175", + "@types/node": "^7.10.2", + "coveralls": "^3.0.6", + "jasmine": "3.5.0", + "jasmine-core": "^3.5.0", + "karma": "^6.3.4", + "karma-cli": "^2.0.0", + "karma-coverage": "^2.0.3", + "karma-jasmine": "^4.0.1", + "karma-phantomjs-launcher": "^1.0.4", + "lodash": "^4.17.21", + "typescript": "^5.3.2", + "uglify-js": "^3.17.4" + }, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + }, + "typings": "./BigInteger.d.ts" +} diff --git a/node_modules/big-integer/tsconfig.json b/node_modules/big-integer/tsconfig.json new file mode 100644 index 0000000..bb8e6cf --- /dev/null +++ b/node_modules/big-integer/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": false, + "baseUrl": "./", + "moduleResolution": "node", + "allowJs": true, + "typeRoots": [ + "./" + ], + "types": [], + "forceConsistentCasingInFileNames": true + }, + "files": [ + "BigInteger.d.ts", + "spec/tsDefinitions.ts" + ] +} diff --git a/node_modules/bplist-parser/.npmignore b/node_modules/bplist-parser/.npmignore new file mode 100644 index 0000000..a9b46ea --- /dev/null +++ b/node_modules/bplist-parser/.npmignore @@ -0,0 +1,8 @@ +/build/* +node_modules +*.node +*.sh +*.swp +.lock* +npm-debug.log +.idea diff --git a/node_modules/bplist-parser/README.md b/node_modules/bplist-parser/README.md new file mode 100644 index 0000000..37e5e1c --- /dev/null +++ b/node_modules/bplist-parser/README.md @@ -0,0 +1,47 @@ +bplist-parser +============= + +Binary Mac OS X Plist (property list) parser. + +## Installation + +```bash +$ npm install bplist-parser +``` + +## Quick Examples + +```javascript +var bplist = require('bplist-parser'); + +bplist.parseFile('myPlist.bplist', function(err, obj) { + if (err) throw err; + + console.log(JSON.stringify(obj)); +}); +``` + +## License + +(The MIT License) + +Copyright (c) 2012 Near Infinity Corporation + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/bplist-parser/bplistParser.js b/node_modules/bplist-parser/bplistParser.js new file mode 100644 index 0000000..f8335bc --- /dev/null +++ b/node_modules/bplist-parser/bplistParser.js @@ -0,0 +1,357 @@ +'use strict'; + +// adapted from http://code.google.com/p/plist/source/browse/trunk/src/com/dd/plist/BinaryPropertyListParser.java + +var fs = require('fs'); +var bigInt = require("big-integer"); +var debug = false; + +exports.maxObjectSize = 100 * 1000 * 1000; // 100Meg +exports.maxObjectCount = 32768; + +// EPOCH = new SimpleDateFormat("yyyy MM dd zzz").parse("2001 01 01 GMT").getTime(); +// ...but that's annoying in a static initializer because it can throw exceptions, ick. +// So we just hardcode the correct value. +var EPOCH = 978307200000; + +// UID object definition +var UID = exports.UID = function(id) { + this.UID = id; +} + +var parseFile = exports.parseFile = function (fileNameOrBuffer, callback) { + function tryParseBuffer(buffer) { + var err = null; + var result; + try { + result = parseBuffer(buffer); + } catch (ex) { + err = ex; + } + callback(err, result); + } + + if (Buffer.isBuffer(fileNameOrBuffer)) { + return tryParseBuffer(fileNameOrBuffer); + } else { + fs.readFile(fileNameOrBuffer, function (err, data) { + if (err) { return callback(err); } + tryParseBuffer(data); + }); + } +}; + +var parseBuffer = exports.parseBuffer = function (buffer) { + var result = {}; + + // check header + var header = buffer.slice(0, 'bplist'.length).toString('utf8'); + if (header !== 'bplist') { + throw new Error("Invalid binary plist. Expected 'bplist' at offset 0."); + } + + // Handle trailer, last 32 bytes of the file + var trailer = buffer.slice(buffer.length - 32, buffer.length); + // 6 null bytes (index 0 to 5) + var offsetSize = trailer.readUInt8(6); + if (debug) { + console.log("offsetSize: " + offsetSize); + } + var objectRefSize = trailer.readUInt8(7); + if (debug) { + console.log("objectRefSize: " + objectRefSize); + } + var numObjects = readUInt64BE(trailer, 8); + if (debug) { + console.log("numObjects: " + numObjects); + } + var topObject = readUInt64BE(trailer, 16); + if (debug) { + console.log("topObject: " + topObject); + } + var offsetTableOffset = readUInt64BE(trailer, 24); + if (debug) { + console.log("offsetTableOffset: " + offsetTableOffset); + } + + if (numObjects > exports.maxObjectCount) { + throw new Error("maxObjectCount exceeded"); + } + + // Handle offset table + var offsetTable = []; + + for (var i = 0; i < numObjects; i++) { + var offsetBytes = buffer.slice(offsetTableOffset + i * offsetSize, offsetTableOffset + (i + 1) * offsetSize); + offsetTable[i] = readUInt(offsetBytes, 0); + if (debug) { + console.log("Offset for Object #" + i + " is " + offsetTable[i] + " [" + offsetTable[i].toString(16) + "]"); + } + } + + // Parses an object inside the currently parsed binary property list. + // For the format specification check + // + // Apple's binary property list parser implementation. + function parseObject(tableOffset) { + var offset = offsetTable[tableOffset]; + var type = buffer[offset]; + var objType = (type & 0xF0) >> 4; //First 4 bits + var objInfo = (type & 0x0F); //Second 4 bits + switch (objType) { + case 0x0: + return parseSimple(); + case 0x1: + return parseInteger(); + case 0x8: + return parseUID(); + case 0x2: + return parseReal(); + case 0x3: + return parseDate(); + case 0x4: + return parseData(); + case 0x5: // ASCII + return parsePlistString(); + case 0x6: // UTF-16 + return parsePlistString(true); + case 0xA: + return parseArray(); + case 0xD: + return parseDictionary(); + default: + throw new Error("Unhandled type 0x" + objType.toString(16)); + } + + function parseSimple() { + //Simple + switch (objInfo) { + case 0x0: // null + return null; + case 0x8: // false + return false; + case 0x9: // true + return true; + case 0xF: // filler byte + return null; + default: + throw new Error("Unhandled simple type 0x" + objType.toString(16)); + } + } + + function bufferToHexString(buffer) { + var str = ''; + var i; + for (i = 0; i < buffer.length; i++) { + if (buffer[i] != 0x00) { + break; + } + } + for (; i < buffer.length; i++) { + var part = '00' + buffer[i].toString(16); + str += part.substr(part.length - 2); + } + return str; + } + + function parseInteger() { + var length = Math.pow(2, objInfo); + if (length > 4) { + var data = buffer.slice(offset + 1, offset + 1 + length); + var str = bufferToHexString(data); + return bigInt(str, 16); + } if (length < exports.maxObjectSize) { + return readUInt(buffer.slice(offset + 1, offset + 1 + length)); + } else { + throw new Error("To little heap space available! Wanted to read " + length + " bytes, but only " + exports.maxObjectSize + " are available."); + } + } + + function parseUID() { + var length = objInfo + 1; + if (length < exports.maxObjectSize) { + return new UID(readUInt(buffer.slice(offset + 1, offset + 1 + length))); + } else { + throw new Error("To little heap space available! Wanted to read " + length + " bytes, but only " + exports.maxObjectSize + " are available."); + } + } + + function parseReal() { + var length = Math.pow(2, objInfo); + if (length < exports.maxObjectSize) { + var realBuffer = buffer.slice(offset + 1, offset + 1 + length); + if (length === 4) { + return realBuffer.readFloatBE(0); + } + else if (length === 8) { + return realBuffer.readDoubleBE(0); + } + } else { + throw new Error("To little heap space available! Wanted to read " + length + " bytes, but only " + exports.maxObjectSize + " are available."); + } + } + + function parseDate() { + if (objInfo != 0x3) { + console.error("Unknown date type :" + objInfo + ". Parsing anyway..."); + } + var dateBuffer = buffer.slice(offset + 1, offset + 9); + return new Date(EPOCH + (1000 * dateBuffer.readDoubleBE(0))); + } + + function parseData() { + var dataoffset = 1; + var length = objInfo; + if (objInfo == 0xF) { + var int_type = buffer[offset + 1]; + var intType = (int_type & 0xF0) / 0x10; + if (intType != 0x1) { + console.error("0x4: UNEXPECTED LENGTH-INT TYPE! " + intType); + } + var intInfo = int_type & 0x0F; + var intLength = Math.pow(2, intInfo); + dataoffset = 2 + intLength; + if (intLength < 3) { + length = readUInt(buffer.slice(offset + 2, offset + 2 + intLength)); + } else { + length = readUInt(buffer.slice(offset + 2, offset + 2 + intLength)); + } + } + if (length < exports.maxObjectSize) { + return buffer.slice(offset + dataoffset, offset + dataoffset + length); + } else { + throw new Error("To little heap space available! Wanted to read " + length + " bytes, but only " + exports.maxObjectSize + " are available."); + } + } + + function parsePlistString (isUtf16) { + isUtf16 = isUtf16 || 0; + var enc = "utf8"; + var length = objInfo; + var stroffset = 1; + if (objInfo == 0xF) { + var int_type = buffer[offset + 1]; + var intType = (int_type & 0xF0) / 0x10; + if (intType != 0x1) { + console.err("UNEXPECTED LENGTH-INT TYPE! " + intType); + } + var intInfo = int_type & 0x0F; + var intLength = Math.pow(2, intInfo); + var stroffset = 2 + intLength; + if (intLength < 3) { + length = readUInt(buffer.slice(offset + 2, offset + 2 + intLength)); + } else { + length = readUInt(buffer.slice(offset + 2, offset + 2 + intLength)); + } + } + // length is String length -> to get byte length multiply by 2, as 1 character takes 2 bytes in UTF-16 + length *= (isUtf16 + 1); + if (length < exports.maxObjectSize) { + var plistString = new Buffer(buffer.slice(offset + stroffset, offset + stroffset + length)); + if (isUtf16) { + plistString = swapBytes(plistString); + enc = "ucs2"; + } + return plistString.toString(enc); + } else { + throw new Error("To little heap space available! Wanted to read " + length + " bytes, but only " + exports.maxObjectSize + " are available."); + } + } + + function parseArray() { + var length = objInfo; + var arrayoffset = 1; + if (objInfo == 0xF) { + var int_type = buffer[offset + 1]; + var intType = (int_type & 0xF0) / 0x10; + if (intType != 0x1) { + console.error("0xa: UNEXPECTED LENGTH-INT TYPE! " + intType); + } + var intInfo = int_type & 0x0F; + var intLength = Math.pow(2, intInfo); + arrayoffset = 2 + intLength; + if (intLength < 3) { + length = readUInt(buffer.slice(offset + 2, offset + 2 + intLength)); + } else { + length = readUInt(buffer.slice(offset + 2, offset + 2 + intLength)); + } + } + if (length * objectRefSize > exports.maxObjectSize) { + throw new Error("To little heap space available!"); + } + var array = []; + for (var i = 0; i < length; i++) { + var objRef = readUInt(buffer.slice(offset + arrayoffset + i * objectRefSize, offset + arrayoffset + (i + 1) * objectRefSize)); + array[i] = parseObject(objRef); + } + return array; + } + + function parseDictionary() { + var length = objInfo; + var dictoffset = 1; + if (objInfo == 0xF) { + var int_type = buffer[offset + 1]; + var intType = (int_type & 0xF0) / 0x10; + if (intType != 0x1) { + console.error("0xD: UNEXPECTED LENGTH-INT TYPE! " + intType); + } + var intInfo = int_type & 0x0F; + var intLength = Math.pow(2, intInfo); + dictoffset = 2 + intLength; + if (intLength < 3) { + length = readUInt(buffer.slice(offset + 2, offset + 2 + intLength)); + } else { + length = readUInt(buffer.slice(offset + 2, offset + 2 + intLength)); + } + } + if (length * 2 * objectRefSize > exports.maxObjectSize) { + throw new Error("To little heap space available!"); + } + if (debug) { + console.log("Parsing dictionary #" + tableOffset); + } + var dict = {}; + for (var i = 0; i < length; i++) { + var keyRef = readUInt(buffer.slice(offset + dictoffset + i * objectRefSize, offset + dictoffset + (i + 1) * objectRefSize)); + var valRef = readUInt(buffer.slice(offset + dictoffset + (length * objectRefSize) + i * objectRefSize, offset + dictoffset + (length * objectRefSize) + (i + 1) * objectRefSize)); + var key = parseObject(keyRef); + var val = parseObject(valRef); + if (debug) { + console.log(" DICT #" + tableOffset + ": Mapped " + key + " to " + val); + } + dict[key] = val; + } + return dict; + } + } + + return [ parseObject(topObject) ]; +}; + +function readUInt(buffer, start) { + start = start || 0; + + var l = 0; + for (var i = start; i < buffer.length; i++) { + l <<= 8; + l |= buffer[i] & 0xFF; + } + return l; +} + +// we're just going to toss the high order bits because javascript doesn't have 64-bit ints +function readUInt64BE(buffer, start) { + var data = buffer.slice(start, start + 8); + return data.readUInt32BE(4, 8); +} + +function swapBytes(buffer) { + var len = buffer.length; + for (var i = 0; i < len; i += 2) { + var a = buffer[i]; + buffer[i] = buffer[i+1]; + buffer[i+1] = a; + } + return buffer; +} diff --git a/node_modules/bplist-parser/package.json b/node_modules/bplist-parser/package.json new file mode 100644 index 0000000..2dae784 --- /dev/null +++ b/node_modules/bplist-parser/package.json @@ -0,0 +1,26 @@ +{ + "name": "bplist-parser", + "version": "0.1.1", + "description": "Binary plist parser.", + "main": "bplistParser.js", + "scripts": { + "test": "./node_modules/nodeunit/bin/nodeunit test" + }, + "keywords": [ + "bplist", + "plist", + "parser" + ], + "author": "Joe Ferner ", + "license": "MIT", + "devDependencies": { + "nodeunit": "~0.9.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/nearinfinity/node-bplist-parser.git" + }, + "dependencies": { + "big-integer": "^1.6.7" + } +} diff --git a/node_modules/bplist-parser/test/airplay.bplist b/node_modules/bplist-parser/test/airplay.bplist new file mode 100644 index 0000000..931adea Binary files /dev/null and b/node_modules/bplist-parser/test/airplay.bplist differ diff --git a/node_modules/bplist-parser/test/iTunes-small.bplist b/node_modules/bplist-parser/test/iTunes-small.bplist new file mode 100644 index 0000000..b7edb14 Binary files /dev/null and b/node_modules/bplist-parser/test/iTunes-small.bplist differ diff --git a/node_modules/bplist-parser/test/int64.bplist b/node_modules/bplist-parser/test/int64.bplist new file mode 100644 index 0000000..6da9c04 Binary files /dev/null and b/node_modules/bplist-parser/test/int64.bplist differ diff --git a/node_modules/bplist-parser/test/int64.xml b/node_modules/bplist-parser/test/int64.xml new file mode 100644 index 0000000..cc6cb03 --- /dev/null +++ b/node_modules/bplist-parser/test/int64.xml @@ -0,0 +1,10 @@ + + + + + zero + 0 + int64item + 12345678901234567890 + + diff --git a/node_modules/bplist-parser/test/parseTest.js b/node_modules/bplist-parser/test/parseTest.js new file mode 100644 index 0000000..67e7bfa --- /dev/null +++ b/node_modules/bplist-parser/test/parseTest.js @@ -0,0 +1,159 @@ +'use strict'; + +// tests are adapted from https://github.com/TooTallNate/node-plist + +var path = require('path'); +var nodeunit = require('nodeunit'); +var bplist = require('../'); + +module.exports = { + 'iTunes Small': function (test) { + var file = path.join(__dirname, "iTunes-small.bplist"); + var startTime1 = new Date(); + + bplist.parseFile(file, function (err, dicts) { + if (err) { + throw err; + } + + var endTime = new Date(); + console.log('Parsed "' + file + '" in ' + (endTime - startTime1) + 'ms'); + var dict = dicts[0]; + test.equal(dict['Application Version'], "9.0.3"); + test.equal(dict['Library Persistent ID'], "6F81D37F95101437"); + test.done(); + }); + }, + + 'sample1': function (test) { + var file = path.join(__dirname, "sample1.bplist"); + var startTime = new Date(); + + bplist.parseFile(file, function (err, dicts) { + if (err) { + throw err; + } + + var endTime = new Date(); + console.log('Parsed "' + file + '" in ' + (endTime - startTime) + 'ms'); + var dict = dicts[0]; + test.equal(dict['CFBundleIdentifier'], 'com.apple.dictionary.MySample'); + test.done(); + }); + }, + + 'sample2': function (test) { + var file = path.join(__dirname, "sample2.bplist"); + var startTime = new Date(); + + bplist.parseFile(file, function (err, dicts) { + if (err) { + throw err; + } + + var endTime = new Date(); + console.log('Parsed "' + file + '" in ' + (endTime - startTime) + 'ms'); + var dict = dicts[0]; + test.equal(dict['PopupMenu'][2]['Key'], "\n #import \n\n#import \n\nint main(int argc, char *argv[])\n{\n return macruby_main(\"rb_main.rb\", argc, argv);\n}\n"); + test.done(); + }); + }, + + 'airplay': function (test) { + var file = path.join(__dirname, "airplay.bplist"); + var startTime = new Date(); + + bplist.parseFile(file, function (err, dicts) { + if (err) { + throw err; + } + + var endTime = new Date(); + console.log('Parsed "' + file + '" in ' + (endTime - startTime) + 'ms'); + + var dict = dicts[0]; + test.equal(dict['duration'], 5555.0495000000001); + test.equal(dict['position'], 4.6269989039999997); + test.done(); + }); + }, + + 'utf16': function (test) { + var file = path.join(__dirname, "utf16.bplist"); + var startTime = new Date(); + + bplist.parseFile(file, function (err, dicts) { + if (err) { + throw err; + } + + var endTime = new Date(); + console.log('Parsed "' + file + '" in ' + (endTime - startTime) + 'ms'); + + var dict = dicts[0]; + test.equal(dict['CFBundleName'], 'sellStuff'); + test.equal(dict['CFBundleShortVersionString'], '2.6.1'); + test.equal(dict['NSHumanReadableCopyright'], '©2008-2012, sellStuff, Inc.'); + test.done(); + }); + }, + + 'utf16chinese': function (test) { + var file = path.join(__dirname, "utf16_chinese.plist"); + var startTime = new Date(); + + bplist.parseFile(file, function (err, dicts) { + if (err) { + throw err; + } + + var endTime = new Date(); + console.log('Parsed "' + file + '" in ' + (endTime - startTime) + 'ms'); + + var dict = dicts[0]; + test.equal(dict['CFBundleName'], '天翼阅读'); + test.equal(dict['CFBundleDisplayName'], '天翼阅读'); + test.done(); + }); + }, + + + + 'uid': function (test) { + var file = path.join(__dirname, "uid.bplist"); + var startTime = new Date(); + + bplist.parseFile(file, function (err, dicts) { + if (err) { + throw err; + } + + var endTime = new Date(); + console.log('Parsed "' + file + '" in ' + (endTime - startTime) + 'ms'); + + var dict = dicts[0]; + test.deepEqual(dict['$objects'][1]['NS.keys'], [{UID:2}, {UID:3}, {UID:4}]); + test.deepEqual(dict['$objects'][1]['NS.objects'], [{UID: 5}, {UID:6}, {UID:7}]); + test.deepEqual(dict['$top']['root'], {UID:1}); + test.done(); + }); + }, + + 'int64': function (test) { + var file = path.join(__dirname, "int64.bplist"); + var startTime = new Date(); + + bplist.parseFile(file, function (err, dicts) { + if (err) { + throw err; + } + + var endTime = new Date(); + console.log('Parsed "' + file + '" in ' + (endTime - startTime) + 'ms'); + var dict = dicts[0]; + test.equal(dict['zero'], '0'); + test.equal(dict['int64item'], '12345678901234567890'); + test.done(); + }); + } +}; diff --git a/node_modules/bplist-parser/test/sample1.bplist b/node_modules/bplist-parser/test/sample1.bplist new file mode 100644 index 0000000..5b808ff Binary files /dev/null and b/node_modules/bplist-parser/test/sample1.bplist differ diff --git a/node_modules/bplist-parser/test/sample2.bplist b/node_modules/bplist-parser/test/sample2.bplist new file mode 100644 index 0000000..fc42979 Binary files /dev/null and b/node_modules/bplist-parser/test/sample2.bplist differ diff --git a/node_modules/bplist-parser/test/uid.bplist b/node_modules/bplist-parser/test/uid.bplist new file mode 100644 index 0000000..59f341e Binary files /dev/null and b/node_modules/bplist-parser/test/uid.bplist differ diff --git a/node_modules/bplist-parser/test/utf16.bplist b/node_modules/bplist-parser/test/utf16.bplist new file mode 100644 index 0000000..ba4bcfa Binary files /dev/null and b/node_modules/bplist-parser/test/utf16.bplist differ diff --git a/node_modules/bplist-parser/test/utf16_chinese.plist b/node_modules/bplist-parser/test/utf16_chinese.plist new file mode 100644 index 0000000..ba1e2d7 Binary files /dev/null and b/node_modules/bplist-parser/test/utf16_chinese.plist differ diff --git a/node_modules/brace-expansion/LICENSE b/node_modules/brace-expansion/LICENSE new file mode 100644 index 0000000..de32266 --- /dev/null +++ b/node_modules/brace-expansion/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/brace-expansion/README.md b/node_modules/brace-expansion/README.md new file mode 100644 index 0000000..6b4e0e1 --- /dev/null +++ b/node_modules/brace-expansion/README.md @@ -0,0 +1,129 @@ +# brace-expansion + +[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html), +as known from sh/bash, in JavaScript. + +[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion) +[![downloads](https://img.shields.io/npm/dm/brace-expansion.svg)](https://www.npmjs.org/package/brace-expansion) +[![Greenkeeper badge](https://badges.greenkeeper.io/juliangruber/brace-expansion.svg)](https://greenkeeper.io/) + +[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion) + +## Example + +```js +var expand = require('brace-expansion'); + +expand('file-{a,b,c}.jpg') +// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg'] + +expand('-v{,,}') +// => ['-v', '-v', '-v'] + +expand('file{0..2}.jpg') +// => ['file0.jpg', 'file1.jpg', 'file2.jpg'] + +expand('file-{a..c}.jpg') +// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg'] + +expand('file{2..0}.jpg') +// => ['file2.jpg', 'file1.jpg', 'file0.jpg'] + +expand('file{0..4..2}.jpg') +// => ['file0.jpg', 'file2.jpg', 'file4.jpg'] + +expand('file-{a..e..2}.jpg') +// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg'] + +expand('file{00..10..5}.jpg') +// => ['file00.jpg', 'file05.jpg', 'file10.jpg'] + +expand('{{A..C},{a..c}}') +// => ['A', 'B', 'C', 'a', 'b', 'c'] + +expand('ppp{,config,oe{,conf}}') +// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf'] +``` + +## API + +```js +var expand = require('brace-expansion'); +``` + +### var expanded = expand(str) + +Return an array of all possible and valid expansions of `str`. If none are +found, `[str]` is returned. + +Valid expansions are: + +```js +/^(.*,)+(.+)?$/ +// {a,b,...} +``` + +A comma separated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`. + +```js +/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/ +// {x..y[..incr]} +``` + +A numeric sequence from `x` to `y` inclusive, with optional increment. +If `x` or `y` start with a leading `0`, all the numbers will be padded +to have equal length. Negative numbers and backwards iteration work too. + +```js +/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/ +// {x..y[..incr]} +``` + +An alphabetic sequence from `x` to `y` inclusive, with optional increment. +`x` and `y` must be exactly one character, and if given, `incr` must be a +number. + +For compatibility reasons, the string `${` is not eligible for brace expansion. + +## Installation + +With [npm](https://npmjs.org) do: + +```bash +npm install brace-expansion +``` + +## Contributors + +- [Julian Gruber](https://github.com/juliangruber) +- [Isaac Z. Schlueter](https://github.com/isaacs) + +## Sponsors + +This module is proudly supported by my [Sponsors](https://github.com/juliangruber/sponsors)! + +Do you want to support modules like this to improve their quality, stability and weigh in on new features? Then please consider donating to my [Patreon](https://www.patreon.com/juliangruber). Not sure how much of my modules you're using? Try [feross/thanks](https://github.com/feross/thanks)! + +## License + +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/brace-expansion/index.js b/node_modules/brace-expansion/index.js new file mode 100644 index 0000000..0478be8 --- /dev/null +++ b/node_modules/brace-expansion/index.js @@ -0,0 +1,201 @@ +var concatMap = require('concat-map'); +var balanced = require('balanced-match'); + +module.exports = expandTop; + +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; + +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); +} + +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); +} + +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} + + +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; + + var parts = []; + var m = balanced('{', '}', str); + + if (!m) + return str.split(','); + + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); + + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } + + parts.push.apply(parts, p); + + return parts; +} + +function expandTop(str) { + if (!str) + return []; + + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.substr(0, 2) === '{}') { + str = '\\{\\}' + str.substr(2); + } + + return expand(escapeBraces(str), true).map(unescapeBraces); +} + +function identity(e) { + return e; +} + +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} + +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} + +function expand(str, isTop) { + var expansions = []; + + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; + + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,.*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); + } + return [str]; + } + + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); + } + } + } + + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. + + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; + + var N; + + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); + + N = []; + + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); + } + + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } + + return expansions; +} + diff --git a/node_modules/brace-expansion/package.json b/node_modules/brace-expansion/package.json new file mode 100644 index 0000000..a18faa8 --- /dev/null +++ b/node_modules/brace-expansion/package.json @@ -0,0 +1,47 @@ +{ + "name": "brace-expansion", + "description": "Brace expansion as known from sh/bash", + "version": "1.1.11", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/brace-expansion.git" + }, + "homepage": "https://github.com/juliangruber/brace-expansion", + "main": "index.js", + "scripts": { + "test": "tape test/*.js", + "gentest": "bash test/generate.sh", + "bench": "matcha test/perf/bench.js" + }, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + }, + "devDependencies": { + "matcha": "^0.7.0", + "tape": "^4.6.0" + }, + "keywords": [], + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "license": "MIT", + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/8..latest", + "firefox/20..latest", + "firefox/nightly", + "chrome/25..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + } +} diff --git a/node_modules/concat-map/.travis.yml b/node_modules/concat-map/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/node_modules/concat-map/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/node_modules/concat-map/LICENSE b/node_modules/concat-map/LICENSE new file mode 100644 index 0000000..ee27ba4 --- /dev/null +++ b/node_modules/concat-map/LICENSE @@ -0,0 +1,18 @@ +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/concat-map/README.markdown b/node_modules/concat-map/README.markdown new file mode 100644 index 0000000..408f70a --- /dev/null +++ b/node_modules/concat-map/README.markdown @@ -0,0 +1,62 @@ +concat-map +========== + +Concatenative mapdashery. + +[![browser support](http://ci.testling.com/substack/node-concat-map.png)](http://ci.testling.com/substack/node-concat-map) + +[![build status](https://secure.travis-ci.org/substack/node-concat-map.png)](http://travis-ci.org/substack/node-concat-map) + +example +======= + +``` js +var concatMap = require('concat-map'); +var xs = [ 1, 2, 3, 4, 5, 6 ]; +var ys = concatMap(xs, function (x) { + return x % 2 ? [ x - 0.1, x, x + 0.1 ] : []; +}); +console.dir(ys); +``` + +*** + +``` +[ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ] +``` + +methods +======= + +``` js +var concatMap = require('concat-map') +``` + +concatMap(xs, fn) +----------------- + +Return an array of concatenated elements by calling `fn(x, i)` for each element +`x` and each index `i` in the array `xs`. + +When `fn(x, i)` returns an array, its result will be concatenated with the +result array. If `fn(x, i)` returns anything else, that value will be pushed +onto the end of the result array. + +install +======= + +With [npm](http://npmjs.org) do: + +``` +npm install concat-map +``` + +license +======= + +MIT + +notes +===== + +This module was written while sitting high above the ground in a tree. diff --git a/node_modules/concat-map/example/map.js b/node_modules/concat-map/example/map.js new file mode 100644 index 0000000..3365621 --- /dev/null +++ b/node_modules/concat-map/example/map.js @@ -0,0 +1,6 @@ +var concatMap = require('../'); +var xs = [ 1, 2, 3, 4, 5, 6 ]; +var ys = concatMap(xs, function (x) { + return x % 2 ? [ x - 0.1, x, x + 0.1 ] : []; +}); +console.dir(ys); diff --git a/node_modules/concat-map/index.js b/node_modules/concat-map/index.js new file mode 100644 index 0000000..b29a781 --- /dev/null +++ b/node_modules/concat-map/index.js @@ -0,0 +1,13 @@ +module.exports = function (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + var x = fn(xs[i], i); + if (isArray(x)) res.push.apply(res, x); + else res.push(x); + } + return res; +}; + +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; diff --git a/node_modules/concat-map/package.json b/node_modules/concat-map/package.json new file mode 100644 index 0000000..d3640e6 --- /dev/null +++ b/node_modules/concat-map/package.json @@ -0,0 +1,43 @@ +{ + "name" : "concat-map", + "description" : "concatenative mapdashery", + "version" : "0.0.1", + "repository" : { + "type" : "git", + "url" : "git://github.com/substack/node-concat-map.git" + }, + "main" : "index.js", + "keywords" : [ + "concat", + "concatMap", + "map", + "functional", + "higher-order" + ], + "directories" : { + "example" : "example", + "test" : "test" + }, + "scripts" : { + "test" : "tape test/*.js" + }, + "devDependencies" : { + "tape" : "~2.4.0" + }, + "license" : "MIT", + "author" : { + "name" : "James Halliday", + "email" : "mail@substack.net", + "url" : "http://substack.net" + }, + "testling" : { + "files" : "test/*.js", + "browsers" : { + "ie" : [ 6, 7, 8, 9 ], + "ff" : [ 3.5, 10, 15.0 ], + "chrome" : [ 10, 22 ], + "safari" : [ 5.1 ], + "opera" : [ 12 ] + } + } +} diff --git a/node_modules/concat-map/test/map.js b/node_modules/concat-map/test/map.js new file mode 100644 index 0000000..fdbd702 --- /dev/null +++ b/node_modules/concat-map/test/map.js @@ -0,0 +1,39 @@ +var concatMap = require('../'); +var test = require('tape'); + +test('empty or not', function (t) { + var xs = [ 1, 2, 3, 4, 5, 6 ]; + var ixes = []; + var ys = concatMap(xs, function (x, ix) { + ixes.push(ix); + return x % 2 ? [ x - 0.1, x, x + 0.1 ] : []; + }); + t.same(ys, [ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]); + t.same(ixes, [ 0, 1, 2, 3, 4, 5 ]); + t.end(); +}); + +test('always something', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function (x) { + return x === 'b' ? [ 'B', 'B', 'B' ] : [ x ]; + }); + t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]); + t.end(); +}); + +test('scalars', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function (x) { + return x === 'b' ? [ 'B', 'B', 'B' ] : x; + }); + t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]); + t.end(); +}); + +test('undefs', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function () {}); + t.same(ys, [ undefined, undefined, undefined, undefined ]); + t.end(); +}); diff --git a/node_modules/cordova-common/.eslintignore b/node_modules/cordova-common/.eslintignore new file mode 100644 index 0000000..d606f61 --- /dev/null +++ b/node_modules/cordova-common/.eslintignore @@ -0,0 +1 @@ +spec/fixtures/* diff --git a/node_modules/cordova-common/.eslintrc.yml b/node_modules/cordova-common/.eslintrc.yml new file mode 100644 index 0000000..92aaf67 --- /dev/null +++ b/node_modules/cordova-common/.eslintrc.yml @@ -0,0 +1,10 @@ +root: true +extends: semistandard +rules: + indent: + - error + - 4 + camelcase: off + no-unused-vars: + - error + - args: after-used diff --git a/node_modules/cordova-common/.github/ISSUE_TEMPLATE.md b/node_modules/cordova-common/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..3220c25 --- /dev/null +++ b/node_modules/cordova-common/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,42 @@ + + +### Issue Type + + +- [ ] Bug Report +- [ ] Feature Request +- [ ] Support Question + +## Description + +## Information + + +### Command or Code + + +### Environment, Platform, Device + + + + +### Version information + + + + +## Checklist + + +- [ ] I searched for already existing GitHub issues about this +- [ ] I updated all Cordova tooling to their most recent version +- [ ] I included all the necessary information above diff --git a/node_modules/cordova-common/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/node_modules/cordova-common/.github/ISSUE_TEMPLATE/BUG_REPORT.md new file mode 100644 index 0000000..bd8a3ac --- /dev/null +++ b/node_modules/cordova-common/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -0,0 +1,50 @@ +--- +name: 🐛 Bug Report +about: If something isn't working as expected. + +--- + +# Bug Report + +## Problem + +### What is expected to happen? + + + +### What does actually happen? + + + +## Information + + + + +### Command or Code + + + + +### Environment, Platform, Device + + + + +### Version information + + + + +## Checklist + + +- [ ] I searched for existing GitHub issues +- [ ] I updated all Cordova tooling to most recent version +- [ ] I included all the necessary information above diff --git a/node_modules/cordova-common/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/node_modules/cordova-common/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md new file mode 100644 index 0000000..381fc8a --- /dev/null +++ b/node_modules/cordova-common/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -0,0 +1,29 @@ +--- +name: 🚀 Feature Request +about: A suggestion for a new functionality + +--- + +# Feature Request + +## Motivation Behind Feature + + + + +## Feature Description + + + + +## Alternatives or Workarounds + + + diff --git a/node_modules/cordova-common/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md b/node_modules/cordova-common/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md new file mode 100644 index 0000000..516c6e6 --- /dev/null +++ b/node_modules/cordova-common/.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md @@ -0,0 +1,27 @@ +--- +name: 💬 Support Question +about: If you have a question, please check out our Slack or StackOverflow! + +--- + + + +Apache Cordova uses GitHub Issues as a feature request and bug tracker _only_. +For usage and support questions, please check out the resources below. Thanks! + +--- + +You can get answers to your usage and support questions about **Apache Cordova** on: + +* Slack Community Chat: https://cordova.slack.com (you can sign-up at http://slack.cordova.io/) +* StackOverflow: https://stackoverflow.com/questions/tagged/cordova using the tag `cordova` + +--- + +If you are using a tool that uses Cordova internally, like e.g. Ionic, check their support channels: + +* **Ionic Framework** + * [Ionic Community Forum](https://forum.ionicframework.com/) + * [Ionic Worldwide Slack](https://ionicworldwide.herokuapp.com/) +* **PhoneGap** + * [PhoneGap Developer Community](https://forums.adobe.com/community/phonegap) diff --git a/node_modules/cordova-common/.github/PULL_REQUEST_TEMPLATE.md b/node_modules/cordova-common/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..712b2ff --- /dev/null +++ b/node_modules/cordova-common/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ + + +### Platforms affected + + + +### Motivation and Context + + + + + +### Description + + + + +### Testing + + + + +### Checklist + +- [ ] I've run the tests to see all new and existing tests pass +- [ ] I added automated test coverage as appropriate for this change +- [ ] Commit is prefixed with `(platform)` if this change only applies to one platform (e.g. `(android)`) +- [ ] If this Pull Request resolves an issue, I linked to the issue in the text above (and used the correct [keyword to close issues using keywords](https://help.github.com/articles/closing-issues-using-keywords/)) +- [ ] I've updated the documentation if necessary diff --git a/node_modules/cordova-common/.ratignore b/node_modules/cordova-common/.ratignore new file mode 100644 index 0000000..6ad2449 --- /dev/null +++ b/node_modules/cordova-common/.ratignore @@ -0,0 +1,5 @@ +fixtures +coverage +jasmine.json +appveyor.yml +package-lock.json diff --git a/node_modules/cordova-common/.travis.yml b/node_modules/cordova-common/.travis.yml new file mode 100644 index 0000000..2361f5d --- /dev/null +++ b/node_modules/cordova-common/.travis.yml @@ -0,0 +1,22 @@ +language: node_js +sudo: false + +git: + depth: 10 + +node_js: + - 6 + - 8 + - 10 + - 12 + +install: + - npm install + - npm install -g codecov + +script: + - npm test + - npm run cover + +after_script: + - codecov diff --git a/node_modules/cordova-common/README.md b/node_modules/cordova-common/README.md new file mode 100644 index 0000000..422c98b --- /dev/null +++ b/node_modules/cordova-common/README.md @@ -0,0 +1,155 @@ + + +[![Build status](https://ci.appveyor.com/api/projects/status/wxkmo0jalsr8gane?svg=true)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-common/branch/master) +[![Build Status](https://travis-ci.org/apache/cordova-common.svg?branch=master)](https://travis-ci.org/apache/cordova-common) +[![NPM](https://nodei.co/npm/cordova-common.png)](https://nodei.co/npm/cordova-common/) + +# cordova-common +Exposes shared functionality used by [cordova-lib](https://github.com/apache/cordova-lib/) and Cordova platforms. +## Exposed APIs + +### `events` + +Represents special instance of NodeJS EventEmitter which is intended to be used to post events to cordova-lib and cordova-cli + +Usage: +```js +var events = require('cordova-common').events; +events.emit('warn', 'Some warning message') +``` + +There are the following events supported by cordova-cli: `verbose`, `log`, `info`, `warn`, `error`. + +### `CordovaError` + +An error class used by Cordova to throw cordova-specific errors. The CordovaError class is inherited from Error, so CordovaError instances is also valid Error instances (`instanceof` check succeeds). + +Usage: + +```js +var CordovaError = require('cordova-common').CordovaError; +throw new CordovaError('Some error message', SOME_ERR_CODE); +``` + +See [CordovaError](src/CordovaError/CordovaError.js) for supported error codes. + +### `ConfigParser` + +Exposes functionality to deal with cordova project `config.xml` files. For ConfigParser API reference check [ConfigParser Readme](src/ConfigParser/README.md). + +Usage: +```js +var ConfigParser = require('cordova-common').ConfigParser; +var appConfig = new ConfigParser('path/to/cordova-app/config.xml'); +console.log(appconfig.name() + ':' + appConfig.version()); +``` + +### `PluginInfoProvider` and `PluginInfo` + +`PluginInfo` is a wrapper for cordova plugins' `plugin.xml` files. This class may be instantiated directly or via `PluginInfoProvider`. The difference is that `PluginInfoProvider` caches `PluginInfo` instances based on plugin source directory. + +Usage: +```js +var PluginInfo: require('cordova-common').PluginInfo; +var PluginInfoProvider: require('cordova-common').PluginInfoProvider; + +// The following instances are equal +var plugin1 = new PluginInfo('path/to/plugin_directory'); +var plugin2 = new PluginInfoProvider().get('path/to/plugin_directory'); + +console.log('The plugin ' + plugin1.id + ' has version ' + plugin1.version) +``` + +### `ActionStack` + +Utility module for dealing with sequential tasks. Provides a set of tasks that are needed to be done and reverts all tasks that are already completed if one of those tasks fail to complete. Used internally by cordova-lib and platform's plugin installation routines. + +Usage: +```js +var ActionStack = require('cordova-common').ActionStack; +var stack = new ActionStack() + +var action1 = stack.createAction(task1, [], task1_reverter, []); +var action2 = stack.createAction(task2, [], task2_reverter, []); + +stack.push(action1); +stack.push(action2); + +stack.process() +.then(function() { + // all actions succeded +}) +.catch(function(error){ + // One of actions failed with error +}) +``` + +### `superspawn` + +Module for spawning child processes with some advanced logic. + +Usage: +```js +var superspawn = require('cordova-common').superspawn; +superspawn.spawn('adb', ['devices']) +.progress(function(data){ + if (data.stderr) + console.error('"adb devices" raised an error: ' + data.stderr); +}) +.then(function(devices){ + // Do something... +}) +``` + +### `xmlHelpers` + +A set of utility methods for dealing with XML files. + +Usage: +```js +var xml = require('cordova-common').xmlHelpers; + +var xmlDoc1 = xml.parseElementtreeSync('some/xml/file'); +var xmlDoc2 = xml.parseElementtreeSync('another/xml/file'); + +xml.mergeXml(doc1, doc2); // doc2 now contains all the nodes from doc1 +``` + +### Other APIs + +The APIs listed below are also exposed but are intended to be only used internally by cordova plugin installation routines. + +``` +PlatformJson +ConfigChanges +ConfigKeeper +ConfigFile +mungeUtil +``` + +## Setup +* Clone this repository onto your local machine + `git clone https://github.com/apache/cordova-common.git` +* Navigate to cordova-common directory, install dependencies and npm-link + `cd cordova-common && npm install && npm link` +* Navigate to cordova-lib directory and link cordova-common + `cd && npm link cordova-common && npm install` diff --git a/node_modules/cordova-common/RELEASENOTES.md b/node_modules/cordova-common/RELEASENOTES.md new file mode 100644 index 0000000..139eec2 --- /dev/null +++ b/node_modules/cordova-common/RELEASENOTES.md @@ -0,0 +1,175 @@ + +# Cordova-common Release Notes + +### 3.2.1 (Oct 28, 2019) + +* [GH-78](https://github.com/apache/cordova-common/pull/78) (windows) Add `.jsproj` as file extension for XML files ([GH-62](https://github.com/apache/cordova-common/pull/62)) +* [GH-89](https://github.com/apache/cordova-common/pull/89) revert: ([GH-24](https://github.com/apache/cordova-common/pull/24)) [CB-14108](https://issues.apache.org/jira/browse/CB-14108) fix incorrect count in `config_munge` +* [GH-82](https://github.com/apache/cordova-common/pull/82) General cleanup with eslint updates +* [GH-86](https://github.com/apache/cordova-common/pull/86) eslint cleanup fixes: `operator-linebreak` +* [GH-81](https://github.com/apache/cordova-common/pull/81) remove `no-throw-literal` lint exception not needed +* [GH-83](https://github.com/apache/cordova-common/pull/83) Fix ESLint violations where applicable +* [GH-80](https://github.com/apache/cordova-common/pull/80) Update to jasmine 3.4 & fix resulting spec failures +* [GH-79](https://github.com/apache/cordova-common/pull/79) Promise handling cleanup in specs +* [GH-77](https://github.com/apache/cordova-common/pull/77) Do not ignore AppVeyor failures on Node.js 12 + +### 3.2.0 (Jun 12, 2019) +* (ios) plist document not indented with tabs ([#69](https://github.com/apache/cordova-common/pull/69)) +* Update fs-extra to v8 ([#70](https://github.com/apache/cordova-common/pull/70)) +* Add example usage of podspec declarations ([#67](https://github.com/apache/cordova-common/pull/67)) +* implement setPreference and setPlatformPreference ([#63](https://github.com/apache/cordova-common/pull/63)) +* Catch leaked exceptions in superspawn and convert them to rejections ([#66](https://github.com/apache/cordova-common/pull/66)) + +### 3.1.0 (Dec 24, 2018) +* Update Cordova events into a real singleton class ([#60](https://github.com/apache/cordova-common/pull/60)) +* Refactor CordovaLogger to singleton class ([#53](https://github.com/apache/cordova-common/pull/53)) + +### 3.0.0 (Nov 05, 2018) +* [CB-14166](https://issues.apache.org/jira/browse/CB-14166) Use `cross-spawn` for platform-independent spawning +* add `PluginInfo.getPodSpecs` method +* [CB-13496](https://issues.apache.org/jira/browse/CB-13496) Fix greedy regex in plist-helpers +* [CB-14108](https://issues.apache.org/jira/browse/CB-14108) fix incorrect count in config_munge in ios.json and android.json +* [CB-13685](https://issues.apache.org/jira/browse/CB-13685) **Android**: Update ConfigParser for Adaptive Icons +* [CB-10071](https://issues.apache.org/jira/browse/CB-10071) Add BridgingHeader type attributes for header-file +* [CB-12016](https://issues.apache.org/jira/browse/CB-12016) Removed cordova-registry-mapper dependency +* [CB-14099](https://issues.apache.org/jira/browse/CB-14099) **osx**: Fixed Resolve Config Path for OSX +* [CB-14140](https://issues.apache.org/jira/browse/CB-14140) Replace shelljs calls with fs-extra & which + +### 2.2.2 (May 30, 2018) +* [CB-13979](https://issues.apache.org/jira/browse/CB-13979) More consistency for `config.xml` lookups +* [CB-14064](https://issues.apache.org/jira/browse/CB-14064) Remove Node 4 from CI matrix +* [CB-14088](https://issues.apache.org/jira/browse/CB-14088) Update dependencies +* [CB-11691](https://issues.apache.org/jira/browse/CB-11691) Fix for modifying binary plists +* [CB-13770](https://issues.apache.org/jira/browse/CB-13770) Warn when or not found +* [CB-13471](https://issues.apache.org/jira/browse/CB-13471) Fix tests and path issues for **Windows** +* [CB-13471](https://issues.apache.org/jira/browse/CB-13471) added unit test for config file provider +* [CB-13744](https://issues.apache.org/jira/browse/CB-13744) Recognize storyboards as XML files +* [CB-13674](https://issues.apache.org/jira/browse/CB-13674) Incremented package version to -dev + +### 2.2.1 (Dec 14, 2017) +* [CB-13674](https://issues.apache.org/jira/browse/CB-13674): updated dependencies + +### 2.2.0 (Nov 22, 2017) +* [CB-13471](https://issues.apache.org/jira/browse/CB-13471) File Provider fix belongs in cordova-common +* [CB-11244](https://issues.apache.org/jira/browse/CB-11244) Spot fix for upcoming `cordova-android@7` changes. https://github.com/apache/cordova-android/pull/389 + +### 2.1.1 (Oct 04, 2017) +* [CB-13145](https://issues.apache.org/jira/browse/CB-13145) added `getFrameworks` to unit tests +* [CB-13145](https://issues.apache.org/jira/browse/CB-13145) added variable replacing to framework tag + +### 2.1.0 (August 30, 2017) +* [CB-13145](https://issues.apache.org/jira/browse/CB-13145) added variable replacing to `framework` tag +* [CB-13211](https://issues.apache.org/jira/browse/CB-13211) Add `allows-arbitrary-loads-for-media` attribute parsing for `getAccesses` +* [CB-11968](https://issues.apache.org/jira/browse/CB-11968) Added support for `` in `config.xml` +* [CB-12895](https://issues.apache.org/jira/browse/CB-12895) set up `eslint` and removed `jshint` +* [CB-12785](https://issues.apache.org/jira/browse/CB-12785) added `.gitignore`, `travis`, and `appveyor` support +* [CB-12250](https://issues.apache.org/jira/browse/CB-12250) & [CB-12409](https://issues.apache.org/jira/browse/CB-12409) *iOS*: Fix bug with escaping properties from `plist` file +* [CB-12762](https://issues.apache.org/jira/browse/CB-12762) updated `common`, `fetch`, and `serve` `pkgJson` to point `pkgJson` repo items to github mirrors +* [CB-12766](https://issues.apache.org/jira/browse/CB-12766) Consistently write `JSON` with 2 spaces indentation + +### 2.0.3 (May 02, 2017) +* [CB-8978](https://issues.apache.org/jira/browse/CB-8978) Add option to get `resource-file` from `root` +* [CB-11908](https://issues.apache.org/jira/browse/CB-11908) Add tests for `edit-config` in `config.xml` +* [CB-12665](https://issues.apache.org/jira/browse/CB-12665) removed `enginestrict` since it is deprecated + +### 2.0.2 (Apr 14, 2017) +* [CB-11233](https://issues.apache.org/jira/browse/CB-11233) - Support installing frameworks into 'Embedded Binaries' section of the Xcode project +* [CB-10438](https://issues.apache.org/jira/browse/CB-10438) - Install correct dependency version. Removed shell.remove, added pkg.json to dependency tests 1-3, and updated install.js (.replace) to fix tests in uninstall.spec.js and update to workw with jasmine 2.0 +* [CB-11120](https://issues.apache.org/jira/browse/CB-11120) - Allow short/display name in config.xml +* [CB-11346](https://issues.apache.org/jira/browse/CB-11346) - Remove known platforms check +* [CB-11977](https://issues.apache.org/jira/browse/CB-11977) - updated engines and enginescript for common, fetch, and serve + +### 2.0.1 (Mar 09, 2017) +* [CB-12557](https://issues.apache.org/jira/browse/CB-12557) add both stdout and stderr properties to the error object passed to superspawn reject handler. + +### 2.0.0 (Jan 17, 2017) +* [CB-8978](https://issues.apache.org/jira/browse/CB-8978) Add `resource-file` parsing to `config.xml` +* [CB-12018](https://issues.apache.org/jira/browse/CB-12018): updated `jshint` and updated tests to work with `jasmine@2` instead of `jasmine-node` +* [CB-12163](https://issues.apache.org/jira/browse/CB-12163) Add reference attrib to `resource-file` for **Windows** +* Move windows-specific logic to `cordova-windows` +* [CB-12189](https://issues.apache.org/jira/browse/CB-12189) Add implementation attribute to framework + +### 1.5.1 (Oct 12, 2016) +* [CB-12002](https://issues.apache.org/jira/browse/CB-12002) Add `getAllowIntents()` to `ConfigParser` +* [CB-11998](https://issues.apache.org/jira/browse/CB-11998) `cordova platform add` error with `cordova-common@1.5.0` + +### 1.5.0 (Oct 06, 2016) +* [CB-11776](https://issues.apache.org/jira/browse/CB-11776) Add test case for different `edit-config` targets +* [CB-11908](https://issues.apache.org/jira/browse/CB-11908) Add `edit-config` to `config.xml` +* [CB-11936](https://issues.apache.org/jira/browse/CB-11936) Support four new **App Transport Security (ATS)** keys +* update `config.xml` location if it is a **Android Studio** project. +* use `array` methods and `object.keys` for iterating. avoiding `for-in` loops +* [CB-11517](https://issues.apache.org/jira/browse/CB-11517) Allow `.folder` matches +* [CB-11776](https://issues.apache.org/jira/browse/CB-11776) check `edit-config` target exists + +### 1.4.1 (Aug 09, 2016) +* Add general purpose `ConfigParser.getAttribute` API +* [CB-11653](https://issues.apache.org/jira/browse/CB-11653) moved `findProjectRoot` from `cordova-lib` to `cordova-common` +* [CB-11636](https://issues.apache.org/jira/browse/CB-11636) Handle attributes with quotes correctly +* [CB-11645](https://issues.apache.org/jira/browse/CB-11645) added check to see if `getEditConfig` exists before trying to use it +* [CB-9825](https://issues.apache.org/jira/browse/CB-9825) framework tag spec parsing + +### 1.4.0 (Jul 12, 2016) +* [CB-11023](https://issues.apache.org/jira/browse/CB-11023) Add edit-config functionality + +### 1.3.0 (May 12, 2016) +* [CB-11259](https://issues.apache.org/jira/browse/CB-11259): Improving prepare and build logging +* [CB-11194](https://issues.apache.org/jira/browse/CB-11194) Improve cordova load time +* [CB-1117](https://issues.apache.org/jira/browse/CB-1117) Add `FileUpdater` module to `cordova-common`. +* [CB-11131](https://issues.apache.org/jira/browse/CB-11131) Fix `TypeError: message.toUpperCase` is not a function in `CordovaLogger` + +### 1.2.0 (Apr 18, 2016) +* [CB-11022](https://issues.apache.org/jira/browse/CB-11022) Save modulesMetadata to both www and platform_www when necessary +* [CB-10833](https://issues.apache.org/jira/browse/CB-10833) Deduplicate common logic for plugin installation/uninstallation +* [CB-10822](https://issues.apache.org/jira/browse/CB-10822) Manage plugins/modules metadata using PlatformJson +* [CB-10940](https://issues.apache.org/jira/browse/CB-10940) Can't add Android platform from path +* [CB-10965](https://issues.apache.org/jira/browse/CB-10965) xml helper allows multiple instances to be merge in config.xml + +### 1.1.1 (Mar 18, 2016) +* [CB-10694](https://issues.apache.org/jira/browse/CB-10694) Update test to reflect merging of [CB-9264](https://issues.apache.org/jira/browse/CB-9264) fix +* [CB-10694](https://issues.apache.org/jira/browse/CB-10694) Platform-specific configuration preferences don't override global settings +* [CB-9264](https://issues.apache.org/jira/browse/CB-9264) Duplicate entries in `config.xml` +* [CB-10791](https://issues.apache.org/jira/browse/CB-10791) Add `adjustLoggerLevel` to `cordova-common.CordovaLogger` +* [CB-10662](https://issues.apache.org/jira/browse/CB-10662) Add tests for `ConfigParser.getStaticResources` +* [CB-10622](https://issues.apache.org/jira/browse/CB-10622) fix target attribute being ignored for images in `config.xml`. +* [CB-10583](https://issues.apache.org/jira/browse/CB-10583) Protect plugin preferences from adding extra Array properties. + +### 1.1.0 (Feb 16, 2016) +* [CB-10482](https://issues.apache.org/jira/browse/CB-10482) Remove references to windows8 from cordova-lib/cli +* [CB-10430](https://issues.apache.org/jira/browse/CB-10430) Adds forwardEvents method to easily connect two EventEmitters +* [CB-10176](https://issues.apache.org/jira/browse/CB-10176) Adds CordovaLogger class, based on logger module from cordova-cli +* [CB-10052](https://issues.apache.org/jira/browse/CB-10052) Expose child process' io streams via promise progress notification +* [CB-10497](https://issues.apache.org/jira/browse/CB-10497) Prefer .bat over .cmd on windows platform +* [CB-9984](https://issues.apache.org/jira/browse/CB-9984) Bumps plist version and fixes failing cordova-common test + +### 1.0.0 (Oct 29, 2015) + +* [CB-9890](https://issues.apache.org/jira/browse/CB-9890) Documents cordova-common +* [CB-9598](https://issues.apache.org/jira/browse/CB-9598) Correct cordova-lib -> cordova-common in README +* Pick ConfigParser changes from apache@0c3614e +* [CB-9743](https://issues.apache.org/jira/browse/CB-9743) Removes system frameworks handling from ConfigChanges +* [CB-9598](https://issues.apache.org/jira/browse/CB-9598) Cleans out code which has been moved to `cordova-common` +* Pick ConfigParser changes from apache@ddb027b +* Picking CordovaError changes from apache@a3b1fca +* [CB-9598](https://issues.apache.org/jira/browse/CB-9598) Adds tests and fixtures based on existing cordova-lib ones +* [CB-9598](https://issues.apache.org/jira/browse/CB-9598) Initial implementation for cordova-common + diff --git a/node_modules/cordova-common/appveyor.yml b/node_modules/cordova-common/appveyor.yml new file mode 100644 index 0000000..5267502 --- /dev/null +++ b/node_modules/cordova-common/appveyor.yml @@ -0,0 +1,22 @@ +# appveyor file +# http://www.appveyor.com/docs/appveyor-yml + +shallow_clone: true + +environment: + matrix: + - nodejs_version: 6 + - nodejs_version: 8 + - nodejs_version: 10 + - nodejs_version: 12 + +install: + - ps: Install-Product node $env:nodejs_version + - npm install + +build: off + +test_script: + - node --version + - npm --version + - npm test diff --git a/node_modules/cordova-common/cordova-common.js b/node_modules/cordova-common/cordova-common.js new file mode 100644 index 0000000..801d510 --- /dev/null +++ b/node_modules/cordova-common/cordova-common.js @@ -0,0 +1,47 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var addProperty = require('./src/util/addProperty'); + +module.exports = { }; + +addProperty(module, 'events', './src/events'); +addProperty(module, 'superspawn', './src/superspawn'); + +addProperty(module, 'ActionStack', './src/ActionStack'); +addProperty(module, 'CordovaError', './src/CordovaError/CordovaError'); +addProperty(module, 'CordovaLogger', './src/CordovaLogger'); +addProperty(module, 'CordovaCheck', './src/CordovaCheck'); +addProperty(module, 'CordovaExternalToolErrorContext', './src/CordovaError/CordovaExternalToolErrorContext'); +addProperty(module, 'PlatformJson', './src/PlatformJson'); +addProperty(module, 'ConfigParser', './src/ConfigParser/ConfigParser'); +addProperty(module, 'FileUpdater', './src/FileUpdater'); + +addProperty(module, 'PluginInfo', './src/PluginInfo/PluginInfo'); +addProperty(module, 'PluginInfoProvider', './src/PluginInfo/PluginInfoProvider'); + +addProperty(module, 'PluginManager', './src/PluginManager'); + +addProperty(module, 'ConfigChanges', './src/ConfigChanges/ConfigChanges'); +addProperty(module, 'ConfigKeeper', './src/ConfigChanges/ConfigKeeper'); +addProperty(module, 'ConfigFile', './src/ConfigChanges/ConfigFile'); +addProperty(module, 'mungeUtil', './src/ConfigChanges/munge-util'); + +addProperty(module, 'xmlHelpers', './src/util/xml-helpers'); + diff --git a/node_modules/cordova-common/package.json b/node_modules/cordova-common/package.json new file mode 100644 index 0000000..fb0b0e0 --- /dev/null +++ b/node_modules/cordova-common/package.json @@ -0,0 +1,56 @@ +{ + "author": "Apache Software Foundation", + "name": "cordova-common", + "description": "Apache Cordova tools and platforms shared routines", + "license": "Apache-2.0", + "version": "3.2.1", + "repository": { + "type": "git", + "url": "https://github.com/apache/cordova-common" + }, + "bugs": { + "url": "https://issues.apache.org/jira/browse/CB", + "email": "dev@cordova.apache.org" + }, + "main": "cordova-common.js", + "engines": { + "node": ">=6.0.0" + }, + "scripts": { + "test": "npm run eslint && npm run jasmine", + "eslint": "eslint src spec", + "jasmine": "jasmine JASMINE_CONFIG_PATH=spec/support/jasmine.json", + "cover": "istanbul cover --root src --print detail jasmine" + }, + "dependencies": { + "ansi": "^0.3.1", + "bplist-parser": "^0.1.0", + "cross-spawn": "^6.0.5", + "elementtree": "0.1.7", + "endent": "^1.1.1", + "fs-extra": "^8.0.0", + "glob": "^7.1.2", + "minimatch": "^3.0.0", + "plist": "^3.0.1", + "q": "^1.4.1", + "strip-bom": "^3.0.0", + "underscore": "^1.8.3", + "which": "^1.3.0" + }, + "devDependencies": { + "eslint": "^5.0.0", + "eslint-config-semistandard": "^14.0.0", + "eslint-config-standard": "^13.0.1", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-node": "^8.0.0", + "eslint-plugin-promise": "^4.0.0", + "eslint-plugin-standard": "^4.0.0", + "istanbul": "^0.4.5", + "jasmine": "^3.4.0", + "jasmine-spec-reporter": "^4.2.1", + "osenv": "^0.1.3", + "promise-matchers": "^0.9.6", + "rewire": "^4.0.1" + }, + "contributors": [] +} diff --git a/node_modules/cordova-common/src/ActionStack.js b/node_modules/cordova-common/src/ActionStack.js new file mode 100644 index 0000000..6983c5c --- /dev/null +++ b/node_modules/cordova-common/src/ActionStack.js @@ -0,0 +1,85 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +/* jshint quotmark:false */ + +var events = require('./events'); +var Q = require('q'); + +function ActionStack () { + this.stack = []; + this.completed = []; +} + +ActionStack.prototype = { + createAction: function (handler, action_params, reverter, revert_params) { + return { + handler: { + run: handler, + params: action_params + }, + reverter: { + run: reverter, + params: revert_params + } + }; + }, + push: function (tx) { + this.stack.push(tx); + }, + // Returns a promise. + process: function (platform) { + events.emit('verbose', 'Beginning processing of action stack for ' + platform + ' project...'); + + while (this.stack.length) { + var action = this.stack.shift(); + var handler = action.handler.run; + var action_params = action.handler.params; + + try { + handler.apply(null, action_params); + } catch (e) { + events.emit('warn', 'Error during processing of action! Attempting to revert...'); + this.stack.unshift(action); + var issue = 'Uh oh!\n'; + // revert completed tasks + while (this.completed.length) { + var undo = this.completed.shift(); + var revert = undo.reverter.run; + var revert_params = undo.reverter.params; + + try { + revert.apply(null, revert_params); + } catch (err) { + events.emit('warn', 'Error during reversion of action! We probably really messed up your project now, sorry! D:'); + issue += 'A reversion action failed: ' + err.message + '\n'; + } + } + e.message = issue + e.message; + return Q.reject(e); + } + this.completed.push(action); + } + events.emit('verbose', 'Action stack processing complete.'); + + return Q(); + } +}; + +module.exports = ActionStack; diff --git a/node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js b/node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js new file mode 100644 index 0000000..27c2d9e --- /dev/null +++ b/node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js @@ -0,0 +1,426 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +/* + * This module deals with shared configuration / dependency "stuff". That is: + * - XML configuration files such as config.xml, AndroidManifest.xml or WMAppManifest.xml. + * - plist files in iOS + * Essentially, any type of shared resources that we need to handle with awareness + * of how potentially multiple plugins depend on a single shared resource, should be + * handled in this module. + * + * The implementation uses an object as a hash table, with "leaves" of the table tracking + * reference counts. + */ + +var path = require('path'); +var et = require('elementtree'); +var ConfigKeeper = require('./ConfigKeeper'); +var events = require('../events'); + +var mungeutil = require('./munge-util'); +var xml_helpers = require('../util/xml-helpers'); + +exports.PlatformMunger = PlatformMunger; + +exports.process = function (plugins_dir, project_dir, platform, platformJson, pluginInfoProvider) { + var munger = new PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider); + munger.process(plugins_dir); + munger.save_all(); +}; + +/****************************************************************************** +* PlatformMunger class +* +* Can deal with config file of a single project. +* Parsed config files are cached in a ConfigKeeper object. +******************************************************************************/ +function PlatformMunger (platform, project_dir, platformJson, pluginInfoProvider) { + this.platform = platform; + this.project_dir = project_dir; + this.config_keeper = new ConfigKeeper(project_dir); + this.platformJson = platformJson; + this.pluginInfoProvider = pluginInfoProvider; +} + +// Write out all unsaved files. +PlatformMunger.prototype.save_all = PlatformMunger_save_all; +function PlatformMunger_save_all () { + this.config_keeper.save_all(); + this.platformJson.save(); +} + +// Apply a munge object to a single config file. +// The remove parameter tells whether to add the change or remove it. +PlatformMunger.prototype.apply_file_munge = PlatformMunger_apply_file_munge; +function PlatformMunger_apply_file_munge (file, munge, remove) { + var self = this; + + for (var selector in munge.parents) { + for (var xml_child in munge.parents[selector]) { + // this xml child is new, graft it (only if config file exists) + var config_file = self.config_keeper.get(self.project_dir, self.platform, file); + if (config_file.exists) { + if (remove) config_file.prune_child(selector, munge.parents[selector][xml_child]); + else config_file.graft_child(selector, munge.parents[selector][xml_child]); + } else { + events.emit('warn', 'config file ' + file + ' requested for changes not found at ' + config_file.filepath + ', ignoring'); + } + } + } +} + +PlatformMunger.prototype.remove_plugin_changes = remove_plugin_changes; +function remove_plugin_changes (pluginInfo, is_top_level) { + var self = this; + var platform_config = self.platformJson.root; + var plugin_vars = is_top_level + ? platform_config.installed_plugins[pluginInfo.id] + : platform_config.dependent_plugins[pluginInfo.id]; + var edit_config_changes = null; + if (pluginInfo.getEditConfigs) { + edit_config_changes = pluginInfo.getEditConfigs(self.platform); + } + + // get config munge, aka how did this plugin change various config files + var config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars, edit_config_changes); + // global munge looks at all plugins' changes to config files + var global_munge = platform_config.config_munge; + var munge = mungeutil.decrement_munge(global_munge, config_munge); + + for (var file in munge.files) { + self.apply_file_munge(file, munge.files[file], /* remove = */ true); + } + + // Remove from installed_plugins + self.platformJson.removePlugin(pluginInfo.id, is_top_level); + return self; +} + +PlatformMunger.prototype.add_plugin_changes = add_plugin_changes; +function add_plugin_changes (pluginInfo, plugin_vars, is_top_level, should_increment, plugin_force) { + var self = this; + var platform_config = self.platformJson.root; + + var edit_config_changes = null; + if (pluginInfo.getEditConfigs) { + edit_config_changes = pluginInfo.getEditConfigs(self.platform); + } + + var config_munge; + + if (!edit_config_changes || edit_config_changes.length === 0) { + // get config munge, aka how should this plugin change various config files + config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars); + } else { + var isConflictingInfo = is_conflicting(edit_config_changes, platform_config.config_munge, self, plugin_force); + + if (isConflictingInfo.conflictWithConfigxml) { + throw new Error(pluginInfo.id + + ' cannot be added. changes in this plugin conflicts with changes in config.xml. Conflicts must be resolved before plugin can be added.'); + } + if (plugin_force) { + events.emit('warn', '--force is used. edit-config will overwrite conflicts if any. Conflicting plugins may not work as expected.'); + + // remove conflicting munges + var conflict_munge = mungeutil.decrement_munge(platform_config.config_munge, isConflictingInfo.conflictingMunge); + for (var conflict_file in conflict_munge.files) { + self.apply_file_munge(conflict_file, conflict_munge.files[conflict_file], /* remove = */ true); + } + + // force add new munges + config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars, edit_config_changes); + } else if (isConflictingInfo.conflictFound) { + throw new Error('There was a conflict trying to modify attributes with in plugin ' + pluginInfo.id + + '. The conflicting plugin, ' + isConflictingInfo.conflictingPlugin + ', already modified the same attributes. The conflict must be resolved before ' + + pluginInfo.id + ' can be added. You may use --force to add the plugin and overwrite the conflicting attributes.'); + } else { + // no conflicts, will handle edit-config + config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars, edit_config_changes); + } + } + + self = munge_helper(should_increment, self, platform_config, config_munge); + + // Move to installed/dependent_plugins + self.platformJson.addPlugin(pluginInfo.id, plugin_vars || {}, is_top_level); + return self; +} + +// Handle edit-config changes from config.xml +PlatformMunger.prototype.add_config_changes = add_config_changes; +function add_config_changes (config, should_increment) { + var self = this; + var platform_config = self.platformJson.root; + + var config_munge; + var changes = []; + + if (config.getEditConfigs) { + var edit_config_changes = config.getEditConfigs(self.platform); + if (edit_config_changes) { + changes = changes.concat(edit_config_changes); + } + } + + if (config.getConfigFiles) { + var config_files_changes = config.getConfigFiles(self.platform); + if (config_files_changes) { + changes = changes.concat(config_files_changes); + } + } + + if (changes && changes.length > 0) { + var isConflictingInfo = is_conflicting(changes, platform_config.config_munge, self, true /* always force overwrite other edit-config */); + if (isConflictingInfo.conflictFound) { + var conflict_munge; + var conflict_file; + + if (Object.keys(isConflictingInfo.configxmlMunge.files).length !== 0) { + // silently remove conflicting config.xml munges so new munges can be added + conflict_munge = mungeutil.decrement_munge(platform_config.config_munge, isConflictingInfo.configxmlMunge); + for (conflict_file in conflict_munge.files) { + self.apply_file_munge(conflict_file, conflict_munge.files[conflict_file], /* remove = */ true); + } + } + if (Object.keys(isConflictingInfo.conflictingMunge.files).length !== 0) { + events.emit('warn', 'Conflict found, edit-config changes from config.xml will overwrite plugin.xml changes'); + + // remove conflicting plugin.xml munges + conflict_munge = mungeutil.decrement_munge(platform_config.config_munge, isConflictingInfo.conflictingMunge); + for (conflict_file in conflict_munge.files) { + self.apply_file_munge(conflict_file, conflict_munge.files[conflict_file], /* remove = */ true); + } + } + } + } + + // Add config.xml edit-config and config-file munges + config_munge = self.generate_config_xml_munge(config, changes, 'config.xml'); + self = munge_helper(should_increment, self, platform_config, config_munge); + + // Move to installed/dependent_plugins + return self; +} + +function munge_helper (should_increment, self, platform_config, config_munge) { + // global munge looks at all changes to config files + + // TODO: The should_increment param is only used by cordova-cli and is going away soon. + // If should_increment is set to false, avoid modifying the global_munge (use clone) + // and apply the entire config_munge because it's already a proper subset of the global_munge. + var munge, global_munge; + if (should_increment) { + global_munge = platform_config.config_munge; + munge = mungeutil.increment_munge(global_munge, config_munge); + } else { + global_munge = mungeutil.clone_munge(platform_config.config_munge); + munge = config_munge; + } + + for (var file in munge.files) { + self.apply_file_munge(file, munge.files[file]); + } + + return self; +} + +// Load the global munge from platform json and apply all of it. +// Used by cordova prepare to re-generate some config file from platform +// defaults and the global munge. +PlatformMunger.prototype.reapply_global_munge = reapply_global_munge; +function reapply_global_munge () { + var self = this; + + var platform_config = self.platformJson.root; + var global_munge = platform_config.config_munge; + for (var file in global_munge.files) { + self.apply_file_munge(file, global_munge.files[file]); + } + + return self; +} + +// generate_plugin_config_munge +// Generate the munge object from config.xml +PlatformMunger.prototype.generate_config_xml_munge = generate_config_xml_munge; +function generate_config_xml_munge (config, config_changes, type) { + var munge = { files: {} }; + var id; + + if (!config_changes) { + return munge; + } + + if (type === 'config.xml') { + id = type; + } else { + id = config.id; + } + + config_changes.forEach(function (change) { + change.xmls.forEach(function (xml) { + // 1. stringify each xml + var stringified = (new et.ElementTree(xml)).write({ xml_declaration: false }); + // 2. add into munge + if (change.mode) { + mungeutil.deep_add(munge, change.file, change.target, { xml: stringified, count: 1, mode: change.mode, id: id }); + } else { + mungeutil.deep_add(munge, change.target, change.parent, { xml: stringified, count: 1, after: change.after }); + } + }); + }); + return munge; +} + +// generate_plugin_config_munge +// Generate the munge object from plugin.xml + vars +PlatformMunger.prototype.generate_plugin_config_munge = generate_plugin_config_munge; +function generate_plugin_config_munge (pluginInfo, vars, edit_config_changes) { + var self = this; + + vars = vars || {}; + var munge = { files: {} }; + var changes = pluginInfo.getConfigFiles(self.platform); + + if (edit_config_changes) { + Array.prototype.push.apply(changes, edit_config_changes); + } + + changes.forEach(function (change) { + change.xmls.forEach(function (xml) { + // 1. stringify each xml + var stringified = (new et.ElementTree(xml)).write({ xml_declaration: false }); + // interp vars + if (vars) { + Object.keys(vars).forEach(function (key) { + var regExp = new RegExp('\\$' + key, 'g'); + stringified = stringified.replace(regExp, vars[key]); + }); + } + // 2. add into munge + if (change.mode) { + if (change.mode !== 'remove') { + mungeutil.deep_add(munge, change.file, change.target, { xml: stringified, count: 1, mode: change.mode, plugin: pluginInfo.id }); + } + } else { + mungeutil.deep_add(munge, change.target, change.parent, { xml: stringified, count: 1, after: change.after }); + } + }); + }); + return munge; +} + +function is_conflicting (editchanges, config_munge, self, force) { + var files = config_munge.files; + var conflictFound = false; + var conflictWithConfigxml = false; + var conflictingMunge = { files: {} }; + var configxmlMunge = { files: {} }; + var conflictingParent; + var conflictingPlugin; + + editchanges.forEach(function (editchange) { + if (files[editchange.file]) { + var parents = files[editchange.file].parents; + var target = parents[editchange.target]; + + // Check if the edit target will resolve to an existing target + if (!target || target.length === 0) { + var file_xml = self.config_keeper.get(self.project_dir, self.platform, editchange.file).data; + var resolveEditTarget = xml_helpers.resolveParent(file_xml, editchange.target); + var resolveTarget; + + if (resolveEditTarget) { + for (var parent in parents) { + resolveTarget = xml_helpers.resolveParent(file_xml, parent); + if (resolveEditTarget === resolveTarget) { + conflictingParent = parent; + target = parents[parent]; + break; + } + } + } + } else { + conflictingParent = editchange.target; + } + + if (target && target.length !== 0) { + // conflict has been found + conflictFound = true; + + if (editchange.id === 'config.xml') { + if (target[0].id === 'config.xml') { + // Keep track of config.xml/config.xml edit-config conflicts + mungeutil.deep_add(configxmlMunge, editchange.file, conflictingParent, target[0]); + } else { + // Keep track of config.xml x plugin.xml edit-config conflicts + mungeutil.deep_add(conflictingMunge, editchange.file, conflictingParent, target[0]); + } + } else { + if (target[0].id === 'config.xml') { + // plugin.xml cannot overwrite config.xml changes even if --force is used + conflictWithConfigxml = true; + return; + } + + if (force) { + // Need to find all conflicts when --force is used, track conflicting munges + mungeutil.deep_add(conflictingMunge, editchange.file, conflictingParent, target[0]); + } else { + // plugin cannot overwrite other plugin changes without --force + conflictingPlugin = target[0].plugin; + } + } + } + } + }); + + return { conflictFound: conflictFound, + conflictingPlugin: conflictingPlugin, + conflictingMunge: conflictingMunge, + configxmlMunge: configxmlMunge, + conflictWithConfigxml: conflictWithConfigxml + }; +} + +// Go over the prepare queue and apply the config munges for each plugin +// that has been (un)installed. +PlatformMunger.prototype.process = PlatformMunger_process; +function PlatformMunger_process (plugins_dir) { + var self = this; + var platform_config = self.platformJson.root; + + // Uninstallation first + platform_config.prepare_queue.uninstalled.forEach(function (u) { + var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin)); + self.remove_plugin_changes(pluginInfo, u.topLevel); + }); + + // Now handle installation + platform_config.prepare_queue.installed.forEach(function (u) { + var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin)); + self.add_plugin_changes(pluginInfo, u.vars, u.topLevel, true, u.force); + }); + + // Empty out installed/ uninstalled queues. + platform_config.prepare_queue.uninstalled = []; + platform_config.prepare_queue.installed = []; +} +/** ** END of PlatformMunger ****/ diff --git a/node_modules/cordova-common/src/ConfigChanges/ConfigFile.js b/node_modules/cordova-common/src/ConfigChanges/ConfigFile.js new file mode 100644 index 0000000..b346978 --- /dev/null +++ b/node_modules/cordova-common/src/ConfigChanges/ConfigFile.js @@ -0,0 +1,265 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var fs = require('fs-extra'); +var path = require('path'); + +var modules = {}; +var addProperty = require('../util/addProperty'); + +// Use delay loading to ensure plist and other node modules to not get loaded +// on Android, Windows platforms +addProperty(module, 'bplist', 'bplist-parser', modules); +addProperty(module, 'et', 'elementtree', modules); +addProperty(module, 'glob', 'glob', modules); +addProperty(module, 'plist', 'plist', modules); +addProperty(module, 'plist_helpers', '../util/plist-helpers', modules); +addProperty(module, 'xml_helpers', '../util/xml-helpers', modules); + +/****************************************************************************** +* ConfigFile class +* +* Can load and keep various types of config files. Provides some functionality +* specific to some file types such as grafting XML children. In most cases it +* should be instantiated by ConfigKeeper. +* +* For plugin.xml files use as: +* plugin_config = self.config_keeper.get(plugin_dir, '', 'plugin.xml'); +* +* TODO: Consider moving it out to a separate file and maybe partially with +* overrides in platform handlers. +******************************************************************************/ +function ConfigFile (project_dir, platform, file_tag) { + this.project_dir = project_dir; + this.platform = platform; + this.file_tag = file_tag; + this.is_changed = false; + + this.load(); +} + +// ConfigFile.load() +ConfigFile.prototype.load = ConfigFile_load; +function ConfigFile_load () { + var self = this; + + // config file may be in a place not exactly specified in the target + var filepath = self.filepath = resolveConfigFilePath(self.project_dir, self.platform, self.file_tag); + + if (!filepath || !fs.existsSync(filepath)) { + self.exists = false; + return; + } + self.exists = true; + self.mtime = fs.statSync(self.filepath).mtime; + + var ext = path.extname(filepath); + // Windows8 uses an appxmanifest, and wp8 will likely use + // the same in a future release + if (ext === '.xml' || ext === '.appxmanifest' || ext === '.storyboard' || ext === '.jsproj') { + self.type = 'xml'; + self.data = modules.xml_helpers.parseElementtreeSync(filepath); + } else { + // plist file + self.type = 'plist'; + // TODO: isBinaryPlist() reads the file and then parse re-reads it again. + // We always write out text plist, not binary. + // Do we still need to support binary plist? + // If yes, use plist.parseStringSync() and read the file once. + self.data = isBinaryPlist(filepath) + ? modules.bplist.parseBuffer(fs.readFileSync(filepath))[0] + : modules.plist.parse(fs.readFileSync(filepath, 'utf8')); + } +} + +ConfigFile.prototype.save = function ConfigFile_save () { + var self = this; + if (self.type === 'xml') { + fs.writeFileSync(self.filepath, self.data.write({ indent: 4 }), 'utf-8'); + } else { + // plist + var regExp = /[ \t\r\n]+?<\/string>/g; + fs.writeFileSync(self.filepath, modules.plist.build(self.data, { indent: '\t', offset: -1 }).replace(regExp, '')); + } + self.is_changed = false; +}; + +ConfigFile.prototype.graft_child = function ConfigFile_graft_child (selector, xml_child) { + var self = this; + var filepath = self.filepath; + var result; + if (self.type === 'xml') { + var xml_to_graft = [modules.et.XML(xml_child.xml)]; + switch (xml_child.mode) { + case 'merge': + result = modules.xml_helpers.graftXMLMerge(self.data, xml_to_graft, selector, xml_child); + break; + case 'overwrite': + result = modules.xml_helpers.graftXMLOverwrite(self.data, xml_to_graft, selector, xml_child); + break; + case 'remove': + result = modules.xml_helpers.pruneXMLRemove(self.data, selector, xml_to_graft); + break; + default: + result = modules.xml_helpers.graftXML(self.data, xml_to_graft, selector, xml_child.after); + } + if (!result) { + throw new Error('Unable to graft xml at selector "' + selector + '" from "' + filepath + '" during config install'); + } + } else { + // plist file + result = modules.plist_helpers.graftPLIST(self.data, xml_child.xml, selector); + if (!result) { + throw new Error('Unable to graft plist "' + filepath + '" during config install'); + } + } + self.is_changed = true; +}; + +ConfigFile.prototype.prune_child = function ConfigFile_prune_child (selector, xml_child) { + var self = this; + var filepath = self.filepath; + var result; + if (self.type === 'xml') { + var xml_to_graft = [modules.et.XML(xml_child.xml)]; + switch (xml_child.mode) { + case 'merge': + case 'overwrite': + result = modules.xml_helpers.pruneXMLRestore(self.data, selector, xml_child); + break; + case 'remove': + result = modules.xml_helpers.pruneXMLRemove(self.data, selector, xml_to_graft); + break; + default: + result = modules.xml_helpers.pruneXML(self.data, xml_to_graft, selector); + } + } else { + // plist file + result = modules.plist_helpers.prunePLIST(self.data, xml_child.xml, selector); + } + if (!result) { + var err_msg = 'Pruning at selector "' + selector + '" from "' + filepath + '" went bad.'; + throw new Error(err_msg); + } + self.is_changed = true; +}; + +// Some config-file target attributes are not qualified with a full leading directory, or contain wildcards. +// Resolve to a real path in this function. +// TODO: getIOSProjectname is slow because of glob, try to avoid calling it several times per project. +function resolveConfigFilePath (project_dir, platform, file) { + var filepath = path.join(project_dir, file); + var matches; + + file = path.normalize(file); + + if (file.includes('*')) { + // handle wildcards in targets using glob. + matches = modules.glob.sync(path.join(project_dir, '**', file)); + if (matches.length) filepath = matches[0]; + + // [CB-5989] multiple Info.plist files may exist. default to $PROJECT_NAME-Info.plist + if (matches.length > 1 && file.includes('-Info.plist')) { + var plistName = getIOSProjectname(project_dir) + '-Info.plist'; + for (var i = 0; i < matches.length; i++) { + if (matches[i].includes(plistName)) { + filepath = matches[i]; + break; + } + } + } + return filepath; + } + + // XXX this checks for android studio projects + // only if none of the options above are satisfied does this get called + // TODO: Move this out of cordova-common and into the platforms somehow + if (platform === 'android' && !fs.existsSync(filepath)) { + var config_file; + + if (file === 'AndroidManifest.xml') { + filepath = path.join(project_dir, 'app', 'src', 'main', 'AndroidManifest.xml'); + } else if (file.endsWith('config.xml')) { + filepath = path.join(project_dir, 'app', 'src', 'main', 'res', 'xml', 'config.xml'); + } else if (file.endsWith('strings.xml')) { + // Plugins really shouldn't mess with strings.xml, since it's able to be localized + filepath = path.join(project_dir, 'app', 'src', 'main', 'res', 'values', 'strings.xml'); + } else if (file.includes(path.join('res', 'values'))) { + config_file = path.basename(file); + filepath = path.join(project_dir, 'app', 'src', 'main', 'res', 'values', config_file); + } else if (file.includes(path.join('res', 'xml'))) { + // Catch-all for all other stored XML configuration in legacy plugins + config_file = path.basename(file); + filepath = path.join(project_dir, 'app', 'src', 'main', 'res', 'xml', config_file); + } + return filepath; + } + + // special-case config.xml target that is just "config.xml" for other platforms. This should + // be resolved to the real location of the file. + // TODO: Move this out of cordova-common into platforms + if (file === 'config.xml') { + if (platform === 'ubuntu') { + filepath = path.join(project_dir, 'config.xml'); + } else if (platform === 'ios' || platform === 'osx') { + filepath = path.join( + project_dir, + module.exports.getIOSProjectname(project_dir), + 'config.xml' + ); + } else { + matches = modules.glob.sync(path.join(project_dir, '**', 'config.xml')); + if (matches.length) filepath = matches[0]; + } + return filepath; + } + + // None of the special cases matched, returning project_dir/file. + return filepath; +} + +// Find out the real name of an iOS or OSX project +// TODO: glob is slow, need a better way or caching, or avoid using more than once. +function getIOSProjectname (project_dir) { + var matches = modules.glob.sync(path.join(project_dir, '*.xcodeproj')); + var iospath; + if (matches.length === 1) { + iospath = path.basename(matches[0], '.xcodeproj'); + } else { + var msg; + if (matches.length === 0) { + msg = 'Does not appear to be an xcode project, no xcode project file in ' + project_dir; + } else { + msg = 'There are multiple *.xcodeproj dirs in ' + project_dir; + } + throw new Error(msg); + } + return iospath; +} + +// determine if a plist file is binary +function isBinaryPlist (filename) { + // I wish there was a synchronous way to read only the first 6 bytes of a + // file. This is wasteful :/ + var buf = '' + fs.readFileSync(filename, 'utf8'); + // binary plists start with a magic header, "bplist" + return buf.substring(0, 6) === 'bplist'; +} + +module.exports = ConfigFile; +module.exports.isBinaryPlist = isBinaryPlist; +module.exports.getIOSProjectname = getIOSProjectname; +module.exports.resolveConfigFilePath = resolveConfigFilePath; diff --git a/node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js b/node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js new file mode 100644 index 0000000..0ef0435 --- /dev/null +++ b/node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js @@ -0,0 +1,64 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ +/* jshint sub:true */ + +var path = require('path'); +var ConfigFile = require('./ConfigFile'); + +/****************************************************************************** +* ConfigKeeper class +* +* Used to load and store config files to avoid re-parsing and writing them out +* multiple times. +* +* The config files are referred to by a fake path constructed as +* project_dir/platform/file +* where file is the name used for the file in config munges. +******************************************************************************/ +function ConfigKeeper (project_dir, plugins_dir) { + this.project_dir = project_dir; + this.plugins_dir = plugins_dir; + this._cached = {}; +} + +ConfigKeeper.prototype.get = function ConfigKeeper_get (project_dir, platform, file) { + var self = this; + + // This fixes a bug with older plugins - when specifying config xml instead of res/xml/config.xml + // https://issues.apache.org/jira/browse/CB-6414 + if (file === 'config.xml' && platform === 'android') { + file = 'res/xml/config.xml'; + } + var fake_path = path.join(project_dir, platform, file); + + if (self._cached[fake_path]) { + return self._cached[fake_path]; + } + // File was not cached, need to load. + var config_file = new ConfigFile(project_dir, platform, file); + self._cached[fake_path] = config_file; + return config_file; +}; + +ConfigKeeper.prototype.save_all = function ConfigKeeper_save_all () { + var self = this; + Object.keys(self._cached).forEach(function (fake_path) { + var config_file = self._cached[fake_path]; + if (config_file.is_changed) config_file.save(); + }); +}; + +module.exports = ConfigKeeper; diff --git a/node_modules/cordova-common/src/ConfigChanges/munge-util.js b/node_modules/cordova-common/src/ConfigChanges/munge-util.js new file mode 100644 index 0000000..62648d8 --- /dev/null +++ b/node_modules/cordova-common/src/ConfigChanges/munge-util.js @@ -0,0 +1,162 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ +/* jshint sub:true */ + +var _ = require('underscore'); + +// add the count of [key1][key2]...[keyN] to obj +// return true if it didn't exist before +exports.deep_add = function deep_add (obj, keys /* or key1, key2 .... */) { + if (!Array.isArray(keys)) { + keys = Array.prototype.slice.call(arguments, 1); + } + + return exports.process_munge(obj, true/* createParents */, function (parentArray, k) { + var found = _.find(parentArray, function (element) { + return element.xml === k.xml; + }); + if (found) { + found.after = found.after || k.after; + found.count += k.count; + } else { + parentArray.push(k); + } + return !found; + }, keys); +}; + +// decrement the count of [key1][key2]...[keyN] from obj and remove if it reaches 0 +// return true if it was removed or not found +exports.deep_remove = function deep_remove (obj, keys /* or key1, key2 .... */) { + if (!Array.isArray(keys)) { + keys = Array.prototype.slice.call(arguments, 1); + } + + var result = exports.process_munge(obj, false/* createParents */, function (parentArray, k) { + var index = -1; + var found = _.find(parentArray, function (element) { + index++; + return element.xml === k.xml; + }); + if (found) { + if (parentArray[index].oldAttrib) { + k.oldAttrib = _.extend({}, parentArray[index].oldAttrib); + } + found.count -= k.count; + if (found.count > 0) { + return false; + } else { + parentArray.splice(index, 1); + } + } + return undefined; + }, keys); + + return typeof result === 'undefined' ? true : result; +}; + +// search for [key1][key2]...[keyN] +// return the object or undefined if not found +exports.deep_find = function deep_find (obj, keys /* or key1, key2 .... */) { + if (!Array.isArray(keys)) { + keys = Array.prototype.slice.call(arguments, 1); + } + + return exports.process_munge(obj, false/* createParents? */, function (parentArray, k) { + return _.find(parentArray, function (element) { + return element.xml === (k.xml || k); + }); + }, keys); +}; + +// Execute func passing it the parent array and the xmlChild key. +// When createParents is true, add the file and parent items they are missing +// When createParents is false, stop and return undefined if the file and/or parent items are missing + +exports.process_munge = function process_munge (obj, createParents, func, keys /* or key1, key2 .... */) { + if (!Array.isArray(keys)) { + keys = Array.prototype.slice.call(arguments, 1); + } + var k = keys[0]; + if (keys.length === 1) { + return func(obj, k); + } else if (keys.length === 2) { + if (!obj.parents[k] && !createParents) { + return undefined; + } + obj.parents[k] = obj.parents[k] || []; + return exports.process_munge(obj.parents[k], createParents, func, keys.slice(1)); + } else if (keys.length === 3) { + if (!obj.files[k] && !createParents) { + return undefined; + } + obj.files[k] = obj.files[k] || { parents: {} }; + return exports.process_munge(obj.files[k], createParents, func, keys.slice(1)); + } else { + throw new Error('Invalid key format. Must contain at most 3 elements (file, parent, xmlChild).'); + } +}; + +// All values from munge are added to base as +// base[file][selector][child] += munge[file][selector][child] +// Returns a munge object containing values that exist in munge +// but not in base. +exports.increment_munge = function increment_munge (base, munge) { + var diff = { files: {} }; + + for (var file in munge.files) { + for (var selector in munge.files[file].parents) { + for (var xml_child in munge.files[file].parents[selector]) { + var val = munge.files[file].parents[selector][xml_child]; + // if node not in base, add it to diff and base + // else increment it's value in base without adding to diff + var newlyAdded = exports.deep_add(base, [file, selector, val]); + if (newlyAdded) { + exports.deep_add(diff, file, selector, val); + } + } + } + } + return diff; +}; + +// Update the base munge object as +// base[file][selector][child] -= munge[file][selector][child] +// nodes that reached zero value are removed from base and added to the returned munge +// object. +exports.decrement_munge = function decrement_munge (base, munge) { + var zeroed = { files: {} }; + + for (var file in munge.files) { + for (var selector in munge.files[file].parents) { + for (var xml_child in munge.files[file].parents[selector]) { + var val = munge.files[file].parents[selector][xml_child]; + // if node not in base, add it to diff and base + // else increment it's value in base without adding to diff + var removed = exports.deep_remove(base, [file, selector, val]); + if (removed) { + exports.deep_add(zeroed, file, selector, val); + } + } + } + } + return zeroed; +}; + +// For better readability where used +exports.clone_munge = function clone_munge (munge) { + return exports.increment_munge({}, munge); +}; diff --git a/node_modules/cordova-common/src/ConfigParser/ConfigParser.js b/node_modules/cordova-common/src/ConfigParser/ConfigParser.js new file mode 100644 index 0000000..dc1b66d --- /dev/null +++ b/node_modules/cordova-common/src/ConfigParser/ConfigParser.js @@ -0,0 +1,630 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var et = require('elementtree'); +var xml = require('../util/xml-helpers'); +var CordovaError = require('../CordovaError/CordovaError'); +var fs = require('fs-extra'); +var events = require('../events'); + +/** Wraps a config.xml file */ +function ConfigParser (path) { + this.path = path; + try { + this.doc = xml.parseElementtreeSync(path); + this.cdvNamespacePrefix = getCordovaNamespacePrefix(this.doc); + et.register_namespace(this.cdvNamespacePrefix, 'http://cordova.apache.org/ns/1.0'); + } catch (e) { + events.emit('error', 'Parsing ' + path + ' failed'); + throw e; + } + var r = this.doc.getroot(); + if (r.tag !== 'widget') { + throw new CordovaError(path + ' has incorrect root node name (expected "widget", was "' + r.tag + '")'); + } +} + +function getNodeTextSafe (el) { + return el && el.text && el.text.trim(); +} + +function findOrCreate (doc, name) { + var ret = doc.find(name); + if (!ret) { + ret = new et.Element(name); + doc.getroot().append(ret); + } + return ret; +} + +function getCordovaNamespacePrefix (doc) { + var rootAtribs = Object.getOwnPropertyNames(doc.getroot().attrib); + var prefix = 'cdv'; + for (var j = 0; j < rootAtribs.length; j++) { + if (rootAtribs[j].startsWith('xmlns:') && + doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0') { + var strings = rootAtribs[j].split(':'); + prefix = strings[1]; + break; + } + } + return prefix; +} + +/** + * Finds the value of an element's attribute + * @param {String} attributeName Name of the attribute to search for + * @param {Array} elems An array of ElementTree nodes + * @return {String} + */ +function findElementAttributeValue (attributeName, elems) { + elems = Array.isArray(elems) ? elems : [elems]; + + var value = elems.filter(function (elem) { + return elem.attrib.name.toLowerCase() === attributeName.toLowerCase(); + }).map(function (filteredElems) { + return filteredElems.attrib.value; + }).pop(); + + return value || ''; +} + +function removeChildren (el, selector) { + const matches = el.findall(selector); + matches.forEach(child => el.remove(child)); +} + +ConfigParser.prototype = { + getAttribute: function (attr) { + return this.doc.getroot().attrib[attr]; + }, + + packageName: function () { + return this.getAttribute('id'); + }, + setPackageName: function (id) { + this.doc.getroot().attrib['id'] = id; + }, + android_packageName: function () { + return this.getAttribute('android-packageName'); + }, + android_activityName: function () { + return this.getAttribute('android-activityName'); + }, + ios_CFBundleIdentifier: function () { + return this.getAttribute('ios-CFBundleIdentifier'); + }, + name: function () { + return getNodeTextSafe(this.doc.find('name')); + }, + setName: function (name) { + var el = findOrCreate(this.doc, 'name'); + el.text = name; + }, + shortName: function () { + return this.doc.find('name').attrib['short'] || this.name(); + }, + setShortName: function (shortname) { + var el = findOrCreate(this.doc, 'name'); + if (!el.text) { + el.text = shortname; + } + el.attrib['short'] = shortname; + }, + description: function () { + return getNodeTextSafe(this.doc.find('description')); + }, + setDescription: function (text) { + var el = findOrCreate(this.doc, 'description'); + el.text = text; + }, + version: function () { + return this.getAttribute('version'); + }, + windows_packageVersion: function () { + return this.getAttribute('windows-packageVersion'); + }, + android_versionCode: function () { + return this.getAttribute('android-versionCode'); + }, + ios_CFBundleVersion: function () { + return this.getAttribute('ios-CFBundleVersion'); + }, + setVersion: function (value) { + this.doc.getroot().attrib['version'] = value; + }, + author: function () { + return getNodeTextSafe(this.doc.find('author')); + }, + getGlobalPreference: function (name) { + return findElementAttributeValue(name, this.doc.findall('preference')); + }, + setGlobalPreference: function (name, value) { + var pref = this.doc.find('preference[@name="' + name + '"]'); + if (!pref) { + pref = new et.Element('preference'); + pref.attrib.name = name; + this.doc.getroot().append(pref); + } + pref.attrib.value = value; + }, + getPlatformPreference: function (name, platform) { + return findElementAttributeValue(name, this.doc.findall('./platform[@name="' + platform + '"]/preference')); + }, + setPlatformPreference: function (name, platform, value) { + const platformEl = this.doc.find('./platform[@name="' + platform + '"]'); + if (!platformEl) { + throw new CordovaError('platform does not exist (received platform: ' + platform + ')'); + } + const elems = this.doc.findall('./platform[@name="' + platform + '"]/preference'); + let pref = elems.filter(function (elem) { + return elem.attrib.name.toLowerCase() === name.toLowerCase(); + }).pop(); + + if (!pref) { + pref = new et.Element('preference'); + pref.attrib.name = name; + platformEl.append(pref); + } + pref.attrib.value = value; + }, + getPreference: function (name, platform) { + var platformPreference = ''; + + if (platform) { + platformPreference = this.getPlatformPreference(name, platform); + } + + return platformPreference || this.getGlobalPreference(name); + }, + setPreference: function (name, platform, value) { + if (!value) { + value = platform; + platform = undefined; + } + + if (platform) { + this.setPlatformPreference(name, platform, value); + } else { + this.setGlobalPreference(name, value); + } + }, + /** + * Returns all resources for the platform specified. + * @param {String} platform The platform. + * @param {string} resourceName Type of static resources to return. + * "icon" and "splash" currently supported. + * @return {Array} Resources for the platform specified. + */ + getStaticResources: function (platform, resourceName) { + var ret = []; + var staticResources = []; + if (platform) { // platform specific icons + this.doc.findall('./platform[@name="' + platform + '"]/' + resourceName).forEach(function (elt) { + elt.platform = platform; // mark as platform specific resource + staticResources.push(elt); + }); + } + // root level resources + staticResources = staticResources.concat(this.doc.findall(resourceName)); + // parse resource elements + var that = this; + staticResources.forEach(function (elt) { + var res = {}; + res.src = elt.attrib.src; + res.target = elt.attrib.target || undefined; + res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix + ':density'] || elt.attrib['gap:density']; + res.platform = elt.platform || null; // null means icon represents default icon (shared between platforms) + res.width = +elt.attrib.width || undefined; + res.height = +elt.attrib.height || undefined; + res.background = elt.attrib.background || undefined; + res.foreground = elt.attrib.foreground || undefined; + + // default icon + if (!res.width && !res.height && !res.density) { + ret.defaultResource = res; + } + ret.push(res); + }); + + /** + * Returns resource with specified width and/or height. + * @param {number} width Width of resource. + * @param {number} height Height of resource. + * @return {Resource} Resource object or null if not found. + */ + ret.getBySize = function (width, height) { + return ret.filter(function (res) { + if (!res.width && !res.height) { + return false; + } + return ((!res.width || (width === res.width)) && + (!res.height || (height === res.height))); + })[0] || null; + }; + + /** + * Returns resource with specified density. + * @param {string} density Density of resource. + * @return {Resource} Resource object or null if not found. + */ + ret.getByDensity = function (density) { + return ret.filter(function (res) { + return res.density === density; + })[0] || null; + }; + + /** Returns default icons */ + ret.getDefault = function () { + return ret.defaultResource; + }; + + return ret; + }, + + /** + * Returns all icons for specific platform. + * @param {string} platform Platform name + * @return {Resource[]} Array of icon objects. + */ + getIcons: function (platform) { + return this.getStaticResources(platform, 'icon'); + }, + + /** + * Returns all splash images for specific platform. + * @param {string} platform Platform name + * @return {Resource[]} Array of Splash objects. + */ + getSplashScreens: function (platform) { + return this.getStaticResources(platform, 'splash'); + }, + + /** + * Returns all resource-files for a specific platform. + * @param {string} platform Platform name + * @param {boolean} includeGlobal Whether to return resource-files at the + * root level. + * @return {Resource[]} Array of resource file objects. + */ + getFileResources: function (platform, includeGlobal) { + var fileResources = []; + + if (platform) { // platform specific resources + fileResources = this.doc.findall('./platform[@name="' + platform + '"]/resource-file').map(function (tag) { + return { + platform: platform, + src: tag.attrib.src, + target: tag.attrib.target, + versions: tag.attrib.versions, + deviceTarget: tag.attrib['device-target'], + arch: tag.attrib.arch + }; + }); + } + + if (includeGlobal) { + this.doc.findall('resource-file').forEach(function (tag) { + fileResources.push({ + platform: platform || null, + src: tag.attrib.src, + target: tag.attrib.target, + versions: tag.attrib.versions, + deviceTarget: tag.attrib['device-target'], + arch: tag.attrib.arch + }); + }); + } + + return fileResources; + }, + + /** + * Returns all hook scripts for the hook type specified. + * @param {String} hook The hook type. + * @param {Array} platforms Platforms to look for scripts into (root scripts will be included as well). + * @return {Array} Script elements. + */ + getHookScripts: function (hook, platforms) { + var self = this; + var scriptElements = self.doc.findall('./hook'); + + if (platforms) { + platforms.forEach(function (platform) { + scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/hook')); + }); + } + + function filterScriptByHookType (el) { + return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook; + } + + return scriptElements.filter(filterScriptByHookType); + }, + /** + * Returns a list of plugin (IDs). + * + * This function also returns any plugin's that + * were defined using the legacy tags. + * @return {string[]} Array of plugin IDs + */ + getPluginIdList: function () { + var plugins = this.doc.findall('plugin'); + var result = plugins.map(function (plugin) { + return plugin.attrib.name; + }); + var features = this.doc.findall('feature'); + features.forEach(function (element) { + var idTag = element.find('./param[@name="id"]'); + if (idTag) { + result.push(idTag.attrib.value); + } + }); + return result; + }, + getPlugins: function () { + return this.getPluginIdList().map(function (pluginId) { + return this.getPlugin(pluginId); + }, this); + }, + /** + * Adds a plugin element. Does not check for duplicates. + * @name addPlugin + * @function + * @param {object} attributes name and spec are supported + * @param {Array|object} variables name, value or arbitary object + */ + addPlugin: function (attributes, variables) { + if (!attributes && !attributes.name) return; + var el = new et.Element('plugin'); + el.attrib.name = attributes.name; + if (attributes.spec) { + el.attrib.spec = attributes.spec; + } + + // support arbitrary object as variables source + if (variables && typeof variables === 'object' && !Array.isArray(variables)) { + variables = Object.keys(variables) + .map(function (variableName) { + return { name: variableName, value: variables[variableName] }; + }); + } + + if (variables) { + variables.forEach(function (variable) { + el.append(new et.Element('variable', { name: variable.name, value: variable.value })); + }); + } + this.doc.getroot().append(el); + }, + /** + * Retrives the plugin with the given id or null if not found. + * + * This function also returns any plugin's that + * were defined using the legacy tags. + * @name getPlugin + * @function + * @param {String} id + * @returns {object} plugin including any variables + */ + getPlugin: function (id) { + if (!id) { + return undefined; + } + var pluginElement = this.doc.find('./plugin/[@name="' + id + '"]'); + if (pluginElement === null) { + var legacyFeature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..'); + if (legacyFeature) { + events.emit('log', 'Found deprecated feature entry for ' + id + ' in config.xml.'); + return featureToPlugin(legacyFeature); + } + return undefined; + } + var plugin = {}; + + plugin.name = pluginElement.attrib.name; + plugin.spec = pluginElement.attrib.spec || pluginElement.attrib.src || pluginElement.attrib.version; + plugin.variables = {}; + var variableElements = pluginElement.findall('variable'); + variableElements.forEach(function (varElement) { + var name = varElement.attrib.name; + var value = varElement.attrib.value; + if (name) { + plugin.variables[name] = value; + } + }); + return plugin; + }, + /** + * Remove the plugin entry with give name (id). + * + * This function also operates on any plugin's that + * were defined using the legacy tags. + * @name removePlugin + * @function + * @param id name of the plugin + */ + removePlugin: function (id) { + if (!id) return; + const root = this.doc.getroot(); + removeChildren(root, `./plugin/[@name="${id}"]`); + removeChildren(root, `./feature/param[@name="id"][@value="${id}"]/..`); + }, + + // Add any element to the root + addElement: function (name, attributes) { + var el = et.Element(name); + for (var a in attributes) { + el.attrib[a] = attributes[a]; + } + this.doc.getroot().append(el); + }, + + /** + * Adds an engine. Does not check for duplicates. + * @param {String} name the engine name + * @param {String} spec engine source location or version (optional) + */ + addEngine: function (name, spec) { + if (!name) return; + var el = et.Element('engine'); + el.attrib.name = name; + if (spec) { + el.attrib.spec = spec; + } + this.doc.getroot().append(el); + }, + /** + * Removes all the engines with given name + * @param {String} name the engine name. + */ + removeEngine: function (name) { + removeChildren(this.doc.getroot(), `./engine/[@name="${name}"]`); + }, + getEngines: function () { + var engines = this.doc.findall('./engine'); + return engines.map(function (engine) { + var spec = engine.attrib.spec || engine.attrib.version; + return { + name: engine.attrib.name, + spec: spec || null + }; + }); + }, + /* Get all the access tags */ + getAccesses: function () { + var accesses = this.doc.findall('./access'); + return accesses.map(function (access) { + var minimum_tls_version = access.attrib['minimum-tls-version']; /* String */ + var requires_forward_secrecy = access.attrib['requires-forward-secrecy']; /* Boolean */ + var requires_certificate_transparency = access.attrib['requires-certificate-transparency']; /* Boolean */ + var allows_arbitrary_loads_in_web_content = access.attrib['allows-arbitrary-loads-in-web-content']; /* Boolean */ + var allows_arbitrary_loads_in_media = access.attrib['allows-arbitrary-loads-in-media']; /* Boolean (DEPRECATED) */ + var allows_arbitrary_loads_for_media = access.attrib['allows-arbitrary-loads-for-media']; /* Boolean */ + var allows_local_networking = access.attrib['allows-local-networking']; /* Boolean */ + + return { + origin: access.attrib.origin, + minimum_tls_version: minimum_tls_version, + requires_forward_secrecy: requires_forward_secrecy, + requires_certificate_transparency: requires_certificate_transparency, + allows_arbitrary_loads_in_web_content: allows_arbitrary_loads_in_web_content, + allows_arbitrary_loads_in_media: allows_arbitrary_loads_in_media, + allows_arbitrary_loads_for_media: allows_arbitrary_loads_for_media, + allows_local_networking: allows_local_networking + }; + }); + }, + /* Get all the allow-navigation tags */ + getAllowNavigations: function () { + var allow_navigations = this.doc.findall('./allow-navigation'); + return allow_navigations.map(function (allow_navigation) { + var minimum_tls_version = allow_navigation.attrib['minimum-tls-version']; /* String */ + var requires_forward_secrecy = allow_navigation.attrib['requires-forward-secrecy']; /* Boolean */ + var requires_certificate_transparency = allow_navigation.attrib['requires-certificate-transparency']; /* Boolean */ + + return { + href: allow_navigation.attrib.href, + minimum_tls_version: minimum_tls_version, + requires_forward_secrecy: requires_forward_secrecy, + requires_certificate_transparency: requires_certificate_transparency + }; + }); + }, + /* Get all the allow-intent tags */ + getAllowIntents: function () { + var allow_intents = this.doc.findall('./allow-intent'); + return allow_intents.map(function (allow_intent) { + return { + href: allow_intent.attrib.href + }; + }); + }, + /* Get all edit-config tags */ + getEditConfigs: function (platform) { + var platform_edit_configs = this.doc.findall('./platform[@name="' + platform + '"]/edit-config'); + var edit_configs = this.doc.findall('edit-config').concat(platform_edit_configs); + + return edit_configs.map(function (tag) { + var editConfig = + { + file: tag.attrib['file'], + target: tag.attrib['target'], + mode: tag.attrib['mode'], + id: 'config.xml', + xmls: tag.getchildren() + }; + return editConfig; + }); + }, + + /* Get all config-file tags */ + getConfigFiles: function (platform) { + var platform_config_files = this.doc.findall('./platform[@name="' + platform + '"]/config-file'); + var config_files = this.doc.findall('config-file').concat(platform_config_files); + + return config_files.map(function (tag) { + var configFile = + { + target: tag.attrib['target'], + parent: tag.attrib['parent'], + after: tag.attrib['after'], + xmls: tag.getchildren(), + // To support demuxing via versions + versions: tag.attrib['versions'], + deviceTarget: tag.attrib['device-target'] + }; + return configFile; + }); + }, + + write: function () { + fs.writeFileSync(this.path, this.doc.write({ indent: 4 }), 'utf-8'); + } +}; + +function featureToPlugin (featureElement) { + var plugin = {}; + plugin.variables = []; + var pluginVersion, + pluginSrc; + + var nodes = featureElement.findall('param'); + nodes.forEach(function (element) { + var n = element.attrib.name; + var v = element.attrib.value; + if (n === 'id') { + plugin.name = v; + } else if (n === 'version') { + pluginVersion = v; + } else if (n === 'url' || n === 'installPath') { + pluginSrc = v; + } else { + plugin.variables[n] = v; + } + }); + + var spec = pluginSrc || pluginVersion; + if (spec) { + plugin.spec = spec; + } + + return plugin; +} +module.exports = ConfigParser; diff --git a/node_modules/cordova-common/src/CordovaCheck.js b/node_modules/cordova-common/src/CordovaCheck.js new file mode 100644 index 0000000..5b20ade --- /dev/null +++ b/node_modules/cordova-common/src/CordovaCheck.js @@ -0,0 +1,76 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var fs = require('fs-extra'); +var path = require('path'); + +function isRootDir (dir) { + if (fs.existsSync(path.join(dir, 'www'))) { + if (fs.existsSync(path.join(dir, 'config.xml'))) { + // For sure is. + if (fs.existsSync(path.join(dir, 'platforms'))) { + return 2; + } else { + return 1; + } + } + // Might be (or may be under platforms/). + if (fs.existsSync(path.join(dir, 'www', 'config.xml'))) { + return 1; + } + } + return 0; +} + +// Runs up the directory chain looking for a .cordova directory. +// IF it is found we are in a Cordova project. +// Omit argument to use CWD. +function isCordova (dir) { + if (!dir) { + // Prefer PWD over cwd so that symlinked dirs within your PWD work correctly (CB-5687). + var pwd = process.env.PWD; + var cwd = process.cwd(); + if (pwd && pwd !== cwd && pwd !== 'undefined') { + return isCordova(pwd) || isCordova(cwd); + } + return isCordova(cwd); + } + var bestReturnValueSoFar = false; + for (var i = 0; i < 1000; ++i) { + var result = isRootDir(dir); + if (result === 2) { + return dir; + } + if (result === 1) { + bestReturnValueSoFar = dir; + } + var parentDir = path.normalize(path.join(dir, '..')); + // Detect fs root. + if (parentDir === dir) { + return bestReturnValueSoFar; + } + dir = parentDir; + } + console.error('Hit an unhandled case in CordovaCheck.isCordova'); + return false; +} + +module.exports = { + findProjectRoot: isCordova +}; diff --git a/node_modules/cordova-common/src/CordovaError/CordovaError.js b/node_modules/cordova-common/src/CordovaError/CordovaError.js new file mode 100644 index 0000000..34230a7 --- /dev/null +++ b/node_modules/cordova-common/src/CordovaError/CordovaError.js @@ -0,0 +1,91 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var EOL = require('os').EOL; + +/** + * A derived exception class. See usage example in cli.js + * Based on: + * stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/8460753#8460753 + * @param {String} message Error message + * @param {Number} [code=0] Error code + * @param {CordovaExternalToolErrorContext} [context] External tool error context object + * @constructor + */ +function CordovaError (message, code, context) { + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; + this.code = code || CordovaError.UNKNOWN_ERROR; + this.context = context; +} + +// FIXME __proto__ property has been deprecated as of ECMAScript 3.1 +// eslint-disable-next-line no-proto +CordovaError.prototype.__proto__ = Error.prototype; + +// TODO: Extend error codes according the projects specifics +CordovaError.UNKNOWN_ERROR = 0; +CordovaError.EXTERNAL_TOOL_ERROR = 1; + +/** + * Translates instance's error code number into error code name, e.g. 0 -> UNKNOWN_ERROR + * @returns {string} Error code string name + */ +CordovaError.prototype.getErrorCodeName = function () { + for (const key of Object.keys(CordovaError)) { + if (CordovaError[key] === this.code) { + return key; + } + } +}; + +/** + * Converts CordovaError instance to string representation + * @param {Boolean} [isVerbose] Set up verbose mode. Used to provide more + * details including information about error code name and context + * @return {String} Stringified error representation + */ +CordovaError.prototype.toString = function (isVerbose) { + var message = ''; + var codePrefix = ''; + + if (this.code !== CordovaError.UNKNOWN_ERROR) { + codePrefix = 'code: ' + this.code + (isVerbose ? (' (' + this.getErrorCodeName() + ')') : '') + ' '; + } + + if (this.code === CordovaError.EXTERNAL_TOOL_ERROR) { + if (typeof this.context !== 'undefined') { + if (isVerbose) { + message = codePrefix + EOL + this.context.toString(isVerbose) + '\n failed with an error: ' + + this.message + EOL + 'Stack trace: ' + this.stack; + } else { + message = codePrefix + '\'' + this.context.toString(isVerbose) + '\' ' + this.message; + } + } else { + message = 'External tool failed with an error: ' + this.message; + } + } else { + message = isVerbose ? codePrefix + this.stack : codePrefix + this.message; + } + + return message; +}; + +module.exports = CordovaError; diff --git a/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js b/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js new file mode 100644 index 0000000..30699b4 --- /dev/null +++ b/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js @@ -0,0 +1,48 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +/* jshint proto:true */ + +var path = require('path'); + +/** + * @param {String} cmd Command full path + * @param {String[]} args Command args + * @param {String} [cwd] Command working directory + * @constructor + */ +function CordovaExternalToolErrorContext (cmd, args, cwd) { + this.cmd = cmd; + // Helper field for readability + this.cmdShortName = path.basename(cmd); + this.args = args; + this.cwd = cwd; +} + +CordovaExternalToolErrorContext.prototype.toString = function (isVerbose) { + if (isVerbose) { + return 'External tool \'' + this.cmdShortName + '\'' + + '\nCommand full path: ' + this.cmd + '\nCommand args: ' + this.args + + (typeof this.cwd !== 'undefined' ? '\nCommand cwd: ' + this.cwd : ''); + } + + return this.cmdShortName; +}; + +module.exports = CordovaExternalToolErrorContext; diff --git a/node_modules/cordova-common/src/CordovaLogger.js b/node_modules/cordova-common/src/CordovaLogger.js new file mode 100644 index 0000000..85cd3a9 --- /dev/null +++ b/node_modules/cordova-common/src/CordovaLogger.js @@ -0,0 +1,228 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +const ansi = require('ansi'); +const EventEmitter = require('events').EventEmitter; +const EOL = require('os').EOL; +const formatError = require('./util/formatError'); + +const INSTANCE_KEY = Symbol.for('org.apache.cordova.common.CordovaLogger'); + +/** + * @typedef {'verbose'|'normal'|'warn'|'info'|'error'|'results'} CordovaLoggerLevel + */ + +/** + * Implements logging facility that anybody could use. + * + * Should not be instantiated directly! `CordovaLogger.get()` method should be + * used instead to acquire the logger instance. + */ +class CordovaLogger { + // Encapsulate the default logging level values with constants: + static get VERBOSE () { return 'verbose'; } + + static get NORMAL () { return 'normal'; } + + static get WARN () { return 'warn'; } + + static get INFO () { return 'info'; } + + static get ERROR () { return 'error'; } + + static get RESULTS () { return 'results'; } + + /** + * Static method to create new or acquire existing instance. + * + * @returns {CordovaLogger} Logger instance + */ + static get () { + // This singleton instance pattern is based on the ideas from + // https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/ + if (Object.getOwnPropertySymbols(global).indexOf(INSTANCE_KEY) === -1) { + global[INSTANCE_KEY] = new CordovaLogger(); + } + return global[INSTANCE_KEY]; + } + + constructor () { + /** @private */ + this.levels = {}; + /** @private */ + this.colors = {}; + /** @private */ + this.stdout = process.stdout; + /** @private */ + this.stderr = process.stderr; + + /** @private */ + this.stdoutCursor = ansi(this.stdout); + /** @private */ + this.stderrCursor = ansi(this.stderr); + + this.addLevel(CordovaLogger.VERBOSE, 1000, 'grey'); + this.addLevel(CordovaLogger.NORMAL, 2000); + this.addLevel(CordovaLogger.WARN, 2000, 'yellow'); + this.addLevel(CordovaLogger.INFO, 3000, 'blue'); + this.addLevel(CordovaLogger.ERROR, 5000, 'red'); + this.addLevel(CordovaLogger.RESULTS, 10000); + + this.setLevel(CordovaLogger.NORMAL); + } + + /** + * Emits log message to process' stdout/stderr depending on message's + * severity and current log level. If severity is less than current + * logger's level, then the message is ignored. + * + * @param {CordovaLoggerLevel} logLevel - The message's log level. The + * logger should have corresponding level added (via logger.addLevel), + * otherwise `CordovaLogger.NORMAL` level will be used. + * + * @param {string} message - The message, that should be logged to + * process's stdio. + * + * @returns {CordovaLogger} Return the current instance, to allow chaining. + */ + log (logLevel, message) { + // if there is no such logLevel defined, or provided level has + // less severity than active level, then just ignore this call and return + if (!this.levels[logLevel] || this.levels[logLevel] < this.levels[this.logLevel]) { + // return instance to allow to chain calls + return this; + } + + var isVerbose = this.logLevel === CordovaLogger.VERBOSE; + var cursor = this.stdoutCursor; + + if (message instanceof Error || logLevel === CordovaLogger.ERROR) { + message = formatError(message, isVerbose); + cursor = this.stderrCursor; + } + + var color = this.colors[logLevel]; + if (color) { + cursor.bold().fg[color](); + } + + cursor.write(message).reset().write(EOL); + + return this; + } + + /** + * Adds a new level to logger instance. + * + * This method also creates a shortcut method to log events with the level + * provided. + * (i.e. after adding new level 'debug', the method `logger.debug(message)` + * will exist, equal to `logger.log('debug', message)`) + * + * @param {CordovaLoggerLevel} level - A log level name. The levels with + * the following names are added by default to every instance: 'verbose', + * 'normal', 'warn', 'info', 'error', 'results'. + * + * @param {number} severity - A number that represents level's severity. + * + * @param {string} color - A valid color name, that will be used to log + * messages with this level. Any CSS color code or RGB value is allowed + * (according to ansi documentation: + * https://github.com/TooTallNate/ansi.js#features). + * + * @returns {CordovaLogger} Return the current instance, to allow chaining. + */ + addLevel (level, severity, color) { + this.levels[level] = severity; + + if (color) { + this.colors[level] = color; + } + + // Define own method with corresponding name + if (!this[level]) { + Object.defineProperty(this, level, { + get () { return this.log.bind(this, level); } + }); + } + + return this; + } + + /** + * Sets the current logger level to provided value. + * + * If logger doesn't have level with this name, `CordovaLogger.NORMAL` will + * be used. + * + * @param {CordovaLoggerLevel} logLevel - Level name. The level with this + * name should be added to logger before. + * + * @returns {CordovaLogger} Current instance, to allow chaining. + */ + setLevel (logLevel) { + this.logLevel = this.levels[logLevel] ? logLevel : CordovaLogger.NORMAL; + + return this; + } + + /** + * Adjusts the current logger level according to the passed options. + * + * @param {Object|Array} opts - An object or args array with + * options. + * + * @returns {CordovaLogger} Current instance, to allow chaining. + */ + adjustLevel (opts) { + if (opts.verbose || (Array.isArray(opts) && opts.includes('--verbose'))) { + this.setLevel('verbose'); + } else if (opts.silent || (Array.isArray(opts) && opts.includes('--silent'))) { + this.setLevel('error'); + } + + return this; + } + + /** + * Attaches logger to EventEmitter instance provided. + * + * @param {EventEmitter} eventEmitter - An EventEmitter instance to attach + * the logger to. + * + * @returns {CordovaLogger} Current instance, to allow chaining. + */ + subscribe (eventEmitter) { + if (!(eventEmitter instanceof EventEmitter)) { + throw new Error('Subscribe method only accepts an EventEmitter instance as argument'); + } + + eventEmitter.on('verbose', this.verbose) + .on('log', this.normal) + .on('info', this.info) + .on('warn', this.warn) + .on('warning', this.warn) + // Set up event handlers for logging and results emitted as events. + .on('results', this.results); + + return this; + } +} + +module.exports = CordovaLogger; diff --git a/node_modules/cordova-common/src/FileUpdater.js b/node_modules/cordova-common/src/FileUpdater.js new file mode 100644 index 0000000..6f384b5 --- /dev/null +++ b/node_modules/cordova-common/src/FileUpdater.js @@ -0,0 +1,402 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +'use strict'; + +var fs = require('fs-extra'); +var path = require('path'); +var minimatch = require('minimatch'); + +/** + * Logging callback used in the FileUpdater methods. + * @callback loggingCallback + * @param {string} message A message describing a single file update operation. + */ + +/** + * Updates a target file or directory with a source file or directory. (Directory updates are + * not recursive.) Stats for target and source items must be passed in. This is an internal + * helper function used by other methods in this module. + * + * @param {?string} sourcePath Source file or directory to be used to update the + * destination. If the source is null, then the destination is deleted if it exists. + * @param {?fs.Stats} sourceStats An instance of fs.Stats for the source path, or null if + * the source does not exist. + * @param {string} targetPath Required destination file or directory to be updated. If it does + * not exist, it will be created. + * @param {?fs.Stats} targetStats An instance of fs.Stats for the target path, or null if + * the target does not exist. + * @param {Object} [options] Optional additional parameters for the update. + * @param {string} [options.rootDir] Optional root directory (such as a project) to which target + * and source path parameters are relative; may be omitted if the paths are absolute. The + * rootDir is always omitted from any logged paths, to make the logs easier to read. + * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times. + * Otherwise, a file is copied if the source's last-modified time is greather than or + * equal to the target's last-modified time, or if the file sizes are different. + * @param {loggingCallback} [log] Optional logging callback that takes a string message + * describing any file operations that are performed. + * @return {boolean} true if any changes were made, or false if the force flag is not set + * and everything was up to date + */ +function updatePathWithStats (sourcePath, sourceStats, targetPath, targetStats, options, log) { + var updated = false; + + var rootDir = (options && options.rootDir) || ''; + var copyAll = (options && options.all) || false; + + var targetFullPath = path.join(rootDir || '', targetPath); + + if (sourceStats) { + var sourceFullPath = path.join(rootDir || '', sourcePath); + + if (targetStats && (targetStats.isDirectory() !== sourceStats.isDirectory())) { + // The target exists. But if the directory status doesn't match the source, delete it. + log('delete ' + targetPath); + fs.removeSync(targetFullPath); + targetStats = null; + updated = true; + } + + if (!targetStats) { + if (sourceStats.isDirectory()) { + // The target directory does not exist, so it should be created. + log('mkdir ' + targetPath); + fs.ensureDirSync(targetFullPath); + updated = true; + } else if (sourceStats.isFile()) { + // The target file does not exist, so it should be copied from the source. + log('copy ' + sourcePath + ' ' + targetPath + (copyAll ? '' : ' (new file)')); + fs.copySync(sourceFullPath, targetFullPath); + updated = true; + } + } else if (sourceStats.isFile() && targetStats.isFile()) { + // The source and target paths both exist and are files. + if (copyAll) { + // The caller specified all files should be copied. + log('copy ' + sourcePath + ' ' + targetPath); + fs.copySync(sourceFullPath, targetFullPath); + updated = true; + } else { + // Copy if the source has been modified since it was copied to the target, or if + // the file sizes are different. (The latter catches most cases in which something + // was done to the file after copying.) Comparison is >= rather than > to allow + // for timestamps lacking sub-second precision in some filesystems. + if (sourceStats.mtime.getTime() >= targetStats.mtime.getTime() || + sourceStats.size !== targetStats.size) { + log('copy ' + sourcePath + ' ' + targetPath + ' (updated file)'); + fs.copySync(sourceFullPath, targetFullPath); + updated = true; + } + } + } + } else if (targetStats) { + // The target exists but the source is null, so the target should be deleted. + log('delete ' + targetPath + (copyAll ? '' : ' (no source)')); + fs.removeSync(targetFullPath); + updated = true; + } + + return updated; +} + +/** + * Helper for updatePath and updatePaths functions. Queries stats for source and target + * and ensures target directory exists before copying a file. + */ +function updatePathInternal (sourcePath, targetPath, options, log) { + var rootDir = (options && options.rootDir) || ''; + var targetFullPath = path.join(rootDir, targetPath); + var targetStats = fs.existsSync(targetFullPath) ? fs.statSync(targetFullPath) : null; + var sourceStats = null; + + if (sourcePath) { + // A non-null source path was specified. It should exist. + var sourceFullPath = path.join(rootDir, sourcePath); + if (!fs.existsSync(sourceFullPath)) { + throw new Error('Source path does not exist: ' + sourcePath); + } + + sourceStats = fs.statSync(sourceFullPath); + + // Create the target's parent directory if it doesn't exist. + var parentDir = path.dirname(targetFullPath); + if (!fs.existsSync(parentDir)) { + fs.ensureDirSync(parentDir); + } + } + + return updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, options, log); +} + +/** + * Updates a target file or directory with a source file or directory. (Directory updates are + * not recursive.) + * + * @param {?string} sourcePath Source file or directory to be used to update the + * destination. If the source is null, then the destination is deleted if it exists. + * @param {string} targetPath Required destination file or directory to be updated. If it does + * not exist, it will be created. + * @param {Object} [options] Optional additional parameters for the update. + * @param {string} [options.rootDir] Optional root directory (such as a project) to which target + * and source path parameters are relative; may be omitted if the paths are absolute. The + * rootDir is always omitted from any logged paths, to make the logs easier to read. + * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times. + * Otherwise, a file is copied if the source's last-modified time is greather than or + * equal to the target's last-modified time, or if the file sizes are different. + * @param {loggingCallback} [log] Optional logging callback that takes a string message + * describing any file operations that are performed. + * @return {boolean} true if any changes were made, or false if the force flag is not set + * and everything was up to date + */ +function updatePath (sourcePath, targetPath, options, log) { + if (sourcePath !== null && typeof sourcePath !== 'string') { + throw new Error('A source path (or null) is required.'); + } + + if (!targetPath || typeof targetPath !== 'string') { + throw new Error('A target path is required.'); + } + + log = log || function () { }; + + return updatePathInternal(sourcePath, targetPath, options, log); +} + +/** + * Updates files and directories based on a mapping from target paths to source paths. Targets + * with null sources in the map are deleted. + * + * @param {Object} pathMap A dictionary mapping from target paths to source paths. + * @param {Object} [options] Optional additional parameters for the update. + * @param {string} [options.rootDir] Optional root directory (such as a project) to which target + * and source path parameters are relative; may be omitted if the paths are absolute. The + * rootDir is always omitted from any logged paths, to make the logs easier to read. + * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times. + * Otherwise, a file is copied if the source's last-modified time is greather than or + * equal to the target's last-modified time, or if the file sizes are different. + * @param {loggingCallback} [log] Optional logging callback that takes a string message + * describing any file operations that are performed. + * @return {boolean} true if any changes were made, or false if the force flag is not set + * and everything was up to date + */ +function updatePaths (pathMap, options, log) { + if (!pathMap || typeof pathMap !== 'object' || Array.isArray(pathMap)) { + throw new Error('An object mapping from target paths to source paths is required.'); + } + + log = log || function () { }; + + var updated = false; + + // Iterate in sorted order to ensure directories are created before files under them. + Object.keys(pathMap).sort().forEach(function (targetPath) { + var sourcePath = pathMap[targetPath]; + updated = updatePathInternal(sourcePath, targetPath, options, log) || updated; + }); + + return updated; +} + +/** + * Updates a target directory with merged files and subdirectories from source directories. + * + * @param {string|string[]} sourceDirs Required source directory or array of source directories + * to be merged into the target. The directories are listed in order of precedence; files in + * directories later in the array supersede files in directories earlier in the array + * (regardless of timestamps). + * @param {string} targetDir Required destination directory to be updated. If it does not exist, + * it will be created. If it exists, newer files from source directories will be copied over, + * and files missing in the source directories will be deleted. + * @param {Object} [options] Optional additional parameters for the update. + * @param {string} [options.rootDir] Optional root directory (such as a project) to which target + * and source path parameters are relative; may be omitted if the paths are absolute. The + * rootDir is always omitted from any logged paths, to make the logs easier to read. + * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times. + * Otherwise, a file is copied if the source's last-modified time is greather than or + * equal to the target's last-modified time, or if the file sizes are different. + * @param {string|string[]} [options.include] Optional glob string or array of glob strings that + * are tested against both target and source relative paths to determine if they are included + * in the merge-and-update. If unspecified, all items are included. + * @param {string|string[]} [options.exclude] Optional glob string or array of glob strings that + * are tested against both target and source relative paths to determine if they are excluded + * from the merge-and-update. Exclusions override inclusions. If unspecified, no items are + * excluded. + * @param {loggingCallback} [log] Optional logging callback that takes a string message + * describing any file operations that are performed. + * @return {boolean} true if any changes were made, or false if the force flag is not set + * and everything was up to date + */ +function mergeAndUpdateDir (sourceDirs, targetDir, options, log) { + if (sourceDirs && typeof sourceDirs === 'string') { + sourceDirs = [sourceDirs]; + } else if (!Array.isArray(sourceDirs)) { + throw new Error('A source directory path or array of paths is required.'); + } + + if (!targetDir || typeof targetDir !== 'string') { + throw new Error('A target directory path is required.'); + } + + log = log || function () { }; + + var rootDir = (options && options.rootDir) || ''; + + var include = (options && options.include) || ['**']; + if (typeof include === 'string') { + include = [include]; + } else if (!Array.isArray(include)) { + throw new Error('Include parameter must be a glob string or array of glob strings.'); + } + + var exclude = (options && options.exclude) || []; + if (typeof exclude === 'string') { + exclude = [exclude]; + } else if (!Array.isArray(exclude)) { + throw new Error('Exclude parameter must be a glob string or array of glob strings.'); + } + + // Scan the files in each of the source directories. + var sourceMaps = sourceDirs.map(function (sourceDir) { + return path.join(rootDir, sourceDir); + }).map(function (sourcePath) { + if (!fs.existsSync(sourcePath)) { + throw new Error('Source directory does not exist: ' + sourcePath); + } + return mapDirectory(rootDir, path.relative(rootDir, sourcePath), include, exclude); + }); + + // Scan the files in the target directory, if it exists. + var targetMap = {}; + var targetFullPath = path.join(rootDir, targetDir); + if (fs.existsSync(targetFullPath)) { + targetMap = mapDirectory(rootDir, targetDir, include, exclude); + } + + var pathMap = mergePathMaps(sourceMaps, targetMap, targetDir); + + var updated = false; + + // Iterate in sorted order to ensure directories are created before files under them. + Object.keys(pathMap).sort().forEach(function (subPath) { + var entry = pathMap[subPath]; + updated = updatePathWithStats( + entry.sourcePath, + entry.sourceStats, + entry.targetPath, + entry.targetStats, + options, + log) || updated; + }); + + return updated; +} + +/** + * Creates a dictionary map of all files and directories under a path. + */ +function mapDirectory (rootDir, subDir, include, exclude) { + var dirMap = { '': { subDir: subDir, stats: fs.statSync(path.join(rootDir, subDir)) } }; + mapSubdirectory(rootDir, subDir, '', include, exclude, dirMap); + return dirMap; + + function mapSubdirectory (rootDir, subDir, relativeDir, include, exclude, dirMap) { + var itemMapped = false; + var items = fs.readdirSync(path.join(rootDir, subDir, relativeDir)); + + items.forEach(function (item) { + var relativePath = path.join(relativeDir, item); + if (!matchGlobArray(relativePath, exclude)) { + // Stats obtained here (required at least to know where to recurse in directories) + // are saved for later, where the modified times may also be used. This minimizes + // the number of file I/O operations performed. + var fullPath = path.join(rootDir, subDir, relativePath); + var stats = fs.statSync(fullPath); + + if (stats.isDirectory()) { + // Directories are included if either something under them is included or they + // match an include glob. + if (mapSubdirectory(rootDir, subDir, relativePath, include, exclude, dirMap) || + matchGlobArray(relativePath, include)) { + dirMap[relativePath] = { subDir: subDir, stats: stats }; + itemMapped = true; + } + } else if (stats.isFile()) { + // Files are included only if they match an include glob. + if (matchGlobArray(relativePath, include)) { + dirMap[relativePath] = { subDir: subDir, stats: stats }; + itemMapped = true; + } + } + } + }); + return itemMapped; + } + + function matchGlobArray (path, globs) { + return globs.some(function (elem) { + return minimatch(path, elem, { dot: true }); + }); + } +} + +/** + * Merges together multiple source maps and a target map into a single mapping from + * relative paths to objects with target and source paths and stats. + */ +function mergePathMaps (sourceMaps, targetMap, targetDir) { + // Merge multiple source maps together, along with target path info. + // Entries in later source maps override those in earlier source maps. + // Target stats will be filled in below for targets that exist. + var pathMap = {}; + sourceMaps.forEach(function (sourceMap) { + Object.keys(sourceMap).forEach(function (sourceSubPath) { + var sourceEntry = sourceMap[sourceSubPath]; + pathMap[sourceSubPath] = { + targetPath: path.join(targetDir, sourceSubPath), + targetStats: null, + sourcePath: path.join(sourceEntry.subDir, sourceSubPath), + sourceStats: sourceEntry.stats + }; + }); + }); + + // Fill in target stats for targets that exist, and create entries + // for targets that don't have any corresponding sources. + Object.keys(targetMap).forEach(function (subPath) { + var entry = pathMap[subPath]; + if (entry) { + entry.targetStats = targetMap[subPath].stats; + } else { + pathMap[subPath] = { + targetPath: path.join(targetDir, subPath), + targetStats: targetMap[subPath].stats, + sourcePath: null, + sourceStats: null + }; + } + }); + + return pathMap; +} + +module.exports = { + updatePath: updatePath, + updatePaths: updatePaths, + mergeAndUpdateDir: mergeAndUpdateDir +}; diff --git a/node_modules/cordova-common/src/PlatformJson.js b/node_modules/cordova-common/src/PlatformJson.js new file mode 100644 index 0000000..0a87983 --- /dev/null +++ b/node_modules/cordova-common/src/PlatformJson.js @@ -0,0 +1,262 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var fs = require('fs-extra'); +var path = require('path'); +var endent = require('endent'); +var mungeutil = require('./ConfigChanges/munge-util'); + +function PlatformJson (filePath, platform, root) { + this.filePath = filePath; + this.platform = platform; + this.root = fix_munge(root || {}); +} + +PlatformJson.load = function (plugins_dir, platform) { + var filePath = path.join(plugins_dir, platform + '.json'); + var root = null; + if (fs.existsSync(filePath)) { + root = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + } + return new PlatformJson(filePath, platform, root); +}; + +PlatformJson.prototype.save = function () { + fs.outputJsonSync(this.filePath, this.root, { spaces: 2 }); +}; + +/** + * Indicates whether the specified plugin is installed as a top-level (not as + * dependency to others) + * @method function + * @param {String} pluginId A plugin id to check for. + * @return {Boolean} true if plugin installed as top-level, otherwise false. + */ +PlatformJson.prototype.isPluginTopLevel = function (pluginId) { + return this.root.installed_plugins[pluginId]; +}; + +/** + * Indicates whether the specified plugin is installed as a dependency to other + * plugin. + * @method function + * @param {String} pluginId A plugin id to check for. + * @return {Boolean} true if plugin installed as a dependency, otherwise false. + */ +PlatformJson.prototype.isPluginDependent = function (pluginId) { + return this.root.dependent_plugins[pluginId]; +}; + +/** + * Indicates whether plugin is installed either as top-level or as dependency. + * @method function + * @param {String} pluginId A plugin id to check for. + * @return {Boolean} true if plugin installed, otherwise false. + */ +PlatformJson.prototype.isPluginInstalled = function (pluginId) { + return this.isPluginTopLevel(pluginId) || + this.isPluginDependent(pluginId); +}; + +PlatformJson.prototype.addPlugin = function (pluginId, variables, isTopLevel) { + var pluginsList = isTopLevel + ? this.root.installed_plugins + : this.root.dependent_plugins; + + pluginsList[pluginId] = variables; + + return this; +}; + +/** + * @chaining + * Generates and adds metadata for provided plugin into associated .json file + * + * @param {PluginInfo} pluginInfo A pluginInfo instance to add metadata from + * @returns {this} Current PlatformJson instance to allow calls chaining + */ +PlatformJson.prototype.addPluginMetadata = function (pluginInfo) { + var installedModules = this.root.modules || []; + + var installedPaths = installedModules.map(function (installedModule) { + return installedModule.file; + }); + + var modulesToInstall = pluginInfo.getJsModules(this.platform) + .map(function (module) { + return new ModuleMetadata(pluginInfo.id, module); + }) + .filter(function (metadata) { + // Filter out modules which are already added to metadata + return !installedPaths.includes(metadata.file); + }); + + this.root.modules = installedModules.concat(modulesToInstall); + + this.root.plugin_metadata = this.root.plugin_metadata || {}; + this.root.plugin_metadata[pluginInfo.id] = pluginInfo.version; + + return this; +}; + +PlatformJson.prototype.removePlugin = function (pluginId, isTopLevel) { + var pluginsList = isTopLevel + ? this.root.installed_plugins + : this.root.dependent_plugins; + + delete pluginsList[pluginId]; + + return this; +}; + +/** + * @chaining + * Removes metadata for provided plugin from associated file + * + * @param {PluginInfo} pluginInfo A PluginInfo instance to which modules' metadata + * we need to remove + * + * @returns {this} Current PlatformJson instance to allow calls chaining + */ +PlatformJson.prototype.removePluginMetadata = function (pluginInfo) { + var modulesToRemove = pluginInfo.getJsModules(this.platform) + .map(function (jsModule) { + return ['plugins', pluginInfo.id, jsModule.src].join('/'); + }); + + var installedModules = this.root.modules || []; + this.root.modules = installedModules + .filter(function (installedModule) { + // Leave only those metadatas which 'file' is not in removed modules + return !modulesToRemove.includes(installedModule.file); + }); + + if (this.root.plugin_metadata) { + delete this.root.plugin_metadata[pluginInfo.id]; + } + + return this; +}; + +PlatformJson.prototype.addInstalledPluginToPrepareQueue = function (pluginDirName, vars, is_top_level, force) { + this.root.prepare_queue.installed.push({ plugin: pluginDirName, vars: vars, topLevel: is_top_level, force: force }); +}; + +PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function (pluginId, is_top_level) { + this.root.prepare_queue.uninstalled.push({ plugin: pluginId, id: pluginId, topLevel: is_top_level }); +}; + +/** + * Moves plugin, specified by id to top-level plugins. If plugin is top-level + * already, then does nothing. + * @method function + * @param {String} pluginId A plugin id to make top-level. + * @return {PlatformJson} PlatformJson instance. + */ +PlatformJson.prototype.makeTopLevel = function (pluginId) { + var plugin = this.root.dependent_plugins[pluginId]; + if (plugin) { + delete this.root.dependent_plugins[pluginId]; + this.root.installed_plugins[pluginId] = plugin; + } + return this; +}; + +/** + * Generates a metadata for all installed plugins and js modules. The resultant + * string is ready to be written to 'cordova_plugins.js' + * + * @returns {String} cordova_plugins.js contents + */ +PlatformJson.prototype.generateMetadata = function () { + const stringify = o => JSON.stringify(o, null, 2); + return endent` + cordova.define('cordova/plugin_list', function(require, exports, module) { + module.exports = ${stringify(this.root.modules)}; + module.exports.metadata = ${stringify(this.root.plugin_metadata)}; + }); + `; +}; + +/** + * @chaining + * Generates and then saves metadata to specified file. Doesn't check if file exists. + * + * @param {String} destination File metadata will be written to + * @return {PlatformJson} PlatformJson instance + */ +PlatformJson.prototype.generateAndSaveMetadata = function (destination) { + fs.outputFileSync(destination, this.generateMetadata()); + + return this; +}; + +// convert a munge from the old format ([file][parent][xml] = count) to the current one +function fix_munge (root) { + root.prepare_queue = root.prepare_queue || { installed: [], uninstalled: [] }; + root.config_munge = root.config_munge || { files: {} }; + root.installed_plugins = root.installed_plugins || {}; + root.dependent_plugins = root.dependent_plugins || {}; + + var munge = root.config_munge; + if (!munge.files) { + var new_munge = { files: {} }; + for (var file in munge) { + for (var selector in munge[file]) { + for (var xml_child in munge[file][selector]) { + var val = parseInt(munge[file][selector][xml_child]); + for (var i = 0; i < val; i++) { + mungeutil.deep_add(new_munge, [file, selector, { xml: xml_child, count: val }]); + } + } + } + } + root.config_munge = new_munge; + } + + return root; +} + +/** + * @constructor + * @class ModuleMetadata + * + * Creates a ModuleMetadata object that represents module entry in 'cordova_plugins.js' + * file at run time + * + * @param {String} pluginId Plugin id where this module installed from + * @param (JsModule|Object) jsModule A js-module entry from PluginInfo class to generate metadata for + */ +function ModuleMetadata (pluginId, jsModule) { + if (!pluginId) throw new TypeError('pluginId argument must be a valid plugin id'); + if (!jsModule.src && !jsModule.name) throw new TypeError('jsModule argument must contain src or/and name properties'); + + this.id = pluginId + '.' + (jsModule.name || jsModule.src.match(/([^/]+)\.js/)[1]); + this.file = ['plugins', pluginId, jsModule.src].join('/'); + this.pluginId = pluginId; + + if (jsModule.clobbers && jsModule.clobbers.length > 0) { + this.clobbers = jsModule.clobbers.map(function (o) { return o.target; }); + } + if (jsModule.merges && jsModule.merges.length > 0) { + this.merges = jsModule.merges.map(function (o) { return o.target; }); + } + if (jsModule.runs) { + this.runs = true; + } +} + +module.exports = PlatformJson; diff --git a/node_modules/cordova-common/src/PluginInfo/PluginInfo.js b/node_modules/cordova-common/src/PluginInfo/PluginInfo.js new file mode 100644 index 0000000..0130741 --- /dev/null +++ b/node_modules/cordova-common/src/PluginInfo/PluginInfo.js @@ -0,0 +1,494 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +/* +A class for holidng the information currently stored in plugin.xml +It should also be able to answer questions like whether the plugin +is compatible with a given engine version. + +TODO (kamrik): refactor this to not use sync functions and return promises. +*/ + +var path = require('path'); +var fs = require('fs-extra'); +var xml_helpers = require('../util/xml-helpers'); +var CordovaError = require('../CordovaError/CordovaError'); + +function PluginInfo (dirname) { + var self = this; + + // METHODS + // Defined inside the constructor to avoid the "this" binding problems. + + // tag + // Example: + // Used to require a variable to be specified via --variable when installing the plugin. + // returns { key : default | null} + self.getPreferences = getPreferences; + function getPreferences (platform) { + return _getTags(self._et, 'preference', platform, _parsePreference) + .reduce(function (preferences, pref) { + preferences[pref.preference] = pref.default; + return preferences; + }, {}); + } + + function _parsePreference (prefTag) { + var name = prefTag.attrib.name.toUpperCase(); + var def = prefTag.attrib.default || null; + return { preference: name, default: def }; + } + + // + self.getAssets = getAssets; + function getAssets (platform) { + var assets = _getTags(self._et, 'asset', platform, _parseAsset); + return assets; + } + + function _parseAsset (tag) { + var src = tag.attrib.src; + var target = tag.attrib.target; + + if (!src || !target) { + var msg = + 'Malformed tag. Both "src" and "target" attributes' + + 'must be specified in\n' + + self.filepath + ; + throw new Error(msg); + } + + var asset = { + itemType: 'asset', + src: src, + target: target + }; + return asset; + } + + // + // Example: + // + self.getDependencies = getDependencies; + function getDependencies (platform) { + var deps = _getTags( + self._et, + 'dependency', + platform, + _parseDependency + ); + return deps; + } + + function _parseDependency (tag) { + var dep = + { id: tag.attrib.id, + version: tag.attrib.version || '', + url: tag.attrib.url || '', + subdir: tag.attrib.subdir || '', + commit: tag.attrib.commit + }; + + dep.git_ref = dep.commit; + + if (!dep.id) { + var msg = + ' tag is missing id attribute in ' + + self.filepath + ; + throw new CordovaError(msg); + } + return dep; + } + + // tag + self.getConfigFiles = getConfigFiles; + function getConfigFiles (platform) { + var configFiles = _getTags(self._et, 'config-file', platform, _parseConfigFile); + return configFiles; + } + + function _parseConfigFile (tag) { + var configFile = + { target: tag.attrib['target'], + parent: tag.attrib['parent'], + after: tag.attrib['after'], + xmls: tag.getchildren(), + // To support demuxing via versions + versions: tag.attrib['versions'], + deviceTarget: tag.attrib['device-target'] + }; + return configFile; + } + + self.getEditConfigs = getEditConfigs; + function getEditConfigs (platform) { + var editConfigs = _getTags(self._et, 'edit-config', platform, _parseEditConfigs); + return editConfigs; + } + + function _parseEditConfigs (tag) { + var editConfig = + { file: tag.attrib['file'], + target: tag.attrib['target'], + mode: tag.attrib['mode'], + xmls: tag.getchildren() + }; + return editConfig; + } + + // tags, both global and within a + // TODO (kamrik): Do we ever use under ? Example wanted. + self.getInfo = getInfo; + function getInfo (platform) { + var infos = _getTags( + self._et, + 'info', + platform, + function (elem) { return elem.text; } + ); + // Filter out any undefined or empty strings. + infos = infos.filter(Boolean); + return infos; + } + + // + // Examples: + // + // + self.getSourceFiles = getSourceFiles; + function getSourceFiles (platform) { + var sourceFiles = _getTagsInPlatform(self._et, 'source-file', platform, _parseSourceFile); + return sourceFiles; + } + + function _parseSourceFile (tag) { + return { + itemType: 'source-file', + src: tag.attrib.src, + framework: isStrTrue(tag.attrib.framework), + weak: isStrTrue(tag.attrib.weak), + compilerFlags: tag.attrib['compiler-flags'], + targetDir: tag.attrib['target-dir'] + }; + } + + // + // Example: + // + self.getHeaderFiles = getHeaderFiles; + function getHeaderFiles (platform) { + var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function (tag) { + return { + itemType: 'header-file', + src: tag.attrib.src, + targetDir: tag.attrib['target-dir'], + type: tag.attrib['type'] + }; + }); + return headerFiles; + } + + // + // Example: + // + self.getResourceFiles = getResourceFiles; + function getResourceFiles (platform) { + var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function (tag) { + return { + itemType: 'resource-file', + src: tag.attrib.src, + target: tag.attrib.target, + versions: tag.attrib.versions, + deviceTarget: tag.attrib['device-target'], + arch: tag.attrib.arch, + reference: tag.attrib.reference + }; + }); + return resourceFiles; + } + + // + // Example: + // + self.getLibFiles = getLibFiles; + function getLibFiles (platform) { + var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function (tag) { + return { + itemType: 'lib-file', + src: tag.attrib.src, + arch: tag.attrib.arch, + Include: tag.attrib.Include, + versions: tag.attrib.versions, + deviceTarget: tag.attrib['device-target'] || tag.attrib.target + }; + }); + return libFiles; + } + + // + // Example: + // + // + // + // + // + // + // + // + // + // + // + // + // + // + self.getPodSpecs = getPodSpecs; + function getPodSpecs (platform) { + var podSpecs = _getTagsInPlatform(self._et, 'podspec', platform, function (tag) { + var declarations = null; + var sources = null; + var libraries = null; + var config = tag.find('config'); + var pods = tag.find('pods'); + if (config != null) { + sources = config.findall('source').map(function (t) { + return { + url: t.attrib.url + }; + }).reduce(function (acc, val) { + return Object.assign({}, acc, { [val.url]: { source: val.url } }); + }, {}); + } + if (pods != null) { + declarations = Object.keys(pods.attrib).reduce(function (acc, key) { + return pods.attrib[key] === undefined ? acc : Object.assign({}, acc, { [key]: pods.attrib[key] }); + }, {}); + libraries = pods.findall('pod').map(function (t) { + return Object.keys(t.attrib).reduce(function (acc, key) { + return t.attrib[key] === undefined ? acc : Object.assign({}, acc, { [key]: t.attrib[key] }); + }, {}); + }).reduce(function (acc, val) { + return Object.assign({}, acc, { [val.name]: val }); + }, {}); + } + return { + declarations: declarations, + sources: sources, + libraries: libraries + }; + }); + return podSpecs; + } + + // + // Example: + // + self.getHookScripts = getHookScripts; + function getHookScripts (hook, platforms) { + var scriptElements = self._et.findall('./hook'); + + if (platforms) { + platforms.forEach(function (platform) { + scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/hook')); + }); + } + + function filterScriptByHookType (el) { + return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook; + } + + return scriptElements.filter(filterScriptByHookType); + } + + self.getJsModules = getJsModules; + function getJsModules (platform) { + var modules = _getTags(self._et, 'js-module', platform, _parseJsModule); + return modules; + } + + function _parseJsModule (tag) { + var ret = { + itemType: 'js-module', + name: tag.attrib.name, + src: tag.attrib.src, + clobbers: tag.findall('clobbers').map(function (tag) { return { target: tag.attrib.target }; }), + merges: tag.findall('merges').map(function (tag) { return { target: tag.attrib.target }; }), + runs: tag.findall('runs').length > 0 + }; + + return ret; + } + + self.getEngines = function () { + return self._et.findall('engines/engine').map(function (n) { + return { + name: n.attrib.name, + version: n.attrib.version, + platform: n.attrib.platform, + scriptSrc: n.attrib.scriptSrc + }; + }); + }; + + self.getPlatforms = function () { + return self._et.findall('platform').map(function (n) { + return { name: n.attrib.name }; + }); + }; + + self.getPlatformsArray = function () { + return self._et.findall('platform').map(function (n) { + return n.attrib.name; + }); + }; + + self.getFrameworks = function (platform, options) { + return _getTags(self._et, 'framework', platform, function (el) { + var src = el.attrib.src; + if (options) { + var vars = options.cli_variables || {}; + + if (Object.keys(vars).length === 0) { + // get variable defaults from plugin.xml for removal + vars = self.getPreferences(platform); + } + var regExp; + // Iterate over plugin variables. + // Replace them in framework src if they exist + Object.keys(vars).forEach(function (name) { + if (vars[name]) { + regExp = new RegExp('\\$' + name, 'g'); + src = src.replace(regExp, vars[name]); + } + }); + } + var ret = { + itemType: 'framework', + type: el.attrib.type, + parent: el.attrib.parent, + custom: isStrTrue(el.attrib.custom), + embed: isStrTrue(el.attrib.embed), + src: src, + spec: el.attrib.spec, + weak: isStrTrue(el.attrib.weak), + versions: el.attrib.versions, + targetDir: el.attrib['target-dir'], + deviceTarget: el.attrib['device-target'] || el.attrib.target, + arch: el.attrib.arch, + implementation: el.attrib.implementation + }; + return ret; + }); + }; + + self.getFilesAndFrameworks = getFilesAndFrameworks; + function getFilesAndFrameworks (platform, options) { + // Please avoid changing the order of the calls below, files will be + // installed in this order. + var items = [].concat( + self.getSourceFiles(platform), + self.getHeaderFiles(platform), + self.getResourceFiles(platform), + self.getFrameworks(platform, options), + self.getLibFiles(platform) + ); + return items; + } + /// // End of PluginInfo methods ///// + + /// // PluginInfo Constructor logic ///// + self.filepath = path.join(dirname, 'plugin.xml'); + if (!fs.existsSync(self.filepath)) { + throw new CordovaError('Cannot find plugin.xml for plugin "' + path.basename(dirname) + '". Please try adding it again.'); + } + + self.dir = dirname; + var et = self._et = xml_helpers.parseElementtreeSync(self.filepath); + var pelem = et.getroot(); + self.id = pelem.attrib.id; + self.version = pelem.attrib.version; + + // Optional fields + self.name = pelem.findtext('name'); + self.description = pelem.findtext('description'); + self.license = pelem.findtext('license'); + self.repo = pelem.findtext('repo'); + self.issue = pelem.findtext('issue'); + self.keywords = pelem.findtext('keywords'); + self.info = pelem.findtext('info'); + if (self.keywords) { + self.keywords = self.keywords.split(',').map(function (s) { return s.trim(); }); + } + self.getKeywordsAndPlatforms = function () { + var ret = self.keywords || []; + return ret.concat('ecosystem:cordova').concat(addCordova(self.getPlatformsArray())); + }; +} // End of PluginInfo constructor. + +// Helper function used to prefix every element of an array with cordova- +// Useful when we want to modify platforms to be cordova-platform +function addCordova (someArray) { + var newArray = someArray.map(function (element) { + return 'cordova-' + element; + }); + return newArray; +} + +// Helper function used by most of the getSomething methods of PluginInfo. +// Get all elements of a given name. Both in root and in platform sections +// for the given platform. If transform is given and is a function, it is +// applied to each element. +function _getTags (pelem, tag, platform, transform) { + var platformTag = pelem.find('./platform[@name="' + platform + '"]'); + var tagsInRoot = pelem.findall(tag); + tagsInRoot = tagsInRoot || []; + var tagsInPlatform = platformTag ? platformTag.findall(tag) : []; + var tags = tagsInRoot.concat(tagsInPlatform); + if (typeof transform === 'function') { + tags = tags.map(transform); + } + return tags; +} + +// Same as _getTags() but only looks inside a platform section. +function _getTagsInPlatform (pelem, tag, platform, transform) { + var platformTag = pelem.find('./platform[@name="' + platform + '"]'); + var tags = platformTag ? platformTag.findall(tag) : []; + if (typeof transform === 'function') { + tags = tags.map(transform); + } + return tags; +} + +// Check if x is a string 'true'. +function isStrTrue (x) { + return String(x).toLowerCase() === 'true'; +} + +module.exports = PluginInfo; +// Backwards compat: +PluginInfo.PluginInfo = PluginInfo; +PluginInfo.loadPluginsDir = function (dir) { + var PluginInfoProvider = require('./PluginInfoProvider'); + return new PluginInfoProvider().getAllWithinSearchPath(dir); +}; diff --git a/node_modules/cordova-common/src/PluginInfo/PluginInfoProvider.js b/node_modules/cordova-common/src/PluginInfo/PluginInfoProvider.js new file mode 100644 index 0000000..403167d --- /dev/null +++ b/node_modules/cordova-common/src/PluginInfo/PluginInfoProvider.js @@ -0,0 +1,82 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +/* jshint sub:true, laxcomma:true, laxbreak:true */ + +var fs = require('fs-extra'); +var path = require('path'); +var PluginInfo = require('./PluginInfo'); +var events = require('../events'); + +function PluginInfoProvider () { + this._cache = {}; + this._getAllCache = {}; +} + +PluginInfoProvider.prototype.get = function (dirName) { + var absPath = path.resolve(dirName); + if (!this._cache[absPath]) { + this._cache[absPath] = new PluginInfo(dirName); + } + return this._cache[absPath]; +}; + +// Normally you don't need to put() entries, but it's used +// when copying plugins, and in unit tests. +PluginInfoProvider.prototype.put = function (pluginInfo) { + var absPath = path.resolve(pluginInfo.dir); + this._cache[absPath] = pluginInfo; +}; + +// Used for plugin search path processing. +// Given a dir containing multiple plugins, create a PluginInfo object for +// each of them and return as array. +// Should load them all in parallel and return a promise, but not yet. +PluginInfoProvider.prototype.getAllWithinSearchPath = function (dirName) { + var absPath = path.resolve(dirName); + if (!this._getAllCache[absPath]) { + this._getAllCache[absPath] = getAllHelper(absPath, this); + } + return this._getAllCache[absPath]; +}; + +function getAllHelper (absPath, provider) { + if (!fs.existsSync(absPath)) { + return []; + } + // If dir itself is a plugin, return it in an array with one element. + if (fs.existsSync(path.join(absPath, 'plugin.xml'))) { + return [provider.get(absPath)]; + } + var subdirs = fs.readdirSync(absPath); + var plugins = []; + subdirs.forEach(function (subdir) { + var d = path.join(absPath, subdir); + if (fs.existsSync(path.join(d, 'plugin.xml'))) { + try { + plugins.push(provider.get(d)); + } catch (e) { + events.emit('warn', 'Error parsing ' + path.join(d, 'plugin.xml.\n' + e.stack)); + } + } + }); + return plugins; +} + +module.exports = PluginInfoProvider; diff --git a/node_modules/cordova-common/src/PluginManager.js b/node_modules/cordova-common/src/PluginManager.js new file mode 100644 index 0000000..4e9973b --- /dev/null +++ b/node_modules/cordova-common/src/PluginManager.js @@ -0,0 +1,149 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var Q = require('q'); +var fs = require('fs-extra'); +var path = require('path'); + +var ActionStack = require('./ActionStack'); +var PlatformJson = require('./PlatformJson'); +var CordovaError = require('./CordovaError/CordovaError'); +var PlatformMunger = require('./ConfigChanges/ConfigChanges').PlatformMunger; +var PluginInfoProvider = require('./PluginInfo/PluginInfoProvider'); + +/** + * @constructor + * @class PluginManager + * Represents an entity for adding/removing plugins for platforms + * + * @param {String} platform Platform name + * @param {Object} locations - Platform files and directories + * @param {IDEProject} ideProject The IDE project to add/remove plugin changes to/from + */ +function PluginManager (platform, locations, ideProject) { + this.platform = platform; + this.locations = locations; + this.project = ideProject; + + var platformJson = PlatformJson.load(locations.root, platform); + this.munger = new PlatformMunger(platform, locations.root, platformJson, new PluginInfoProvider()); +} + +/** + * @constructs PluginManager + * A convenience shortcut to new PluginManager(...) + * + * @param {String} platform Platform name + * @param {Object} locations - Platform files and directories + * @param {IDEProject} ideProject The IDE project to add/remove plugin changes to/from + * @returns new PluginManager instance + */ +PluginManager.get = function (platform, locations, ideProject) { + return new PluginManager(platform, locations, ideProject); +}; + +PluginManager.INSTALL = 'install'; +PluginManager.UNINSTALL = 'uninstall'; + +module.exports = PluginManager; + +/** + * Describes and implements common plugin installation/uninstallation routine. The flow is the following: + * * Validate and set defaults for options. Note that options are empty by default. Everything + * needed for platform IDE project must be passed from outside. Plugin variables (which + * are the part of the options) also must be already populated with 'PACKAGE_NAME' variable. + * * Collect all plugin's native and web files, get installers/uninstallers and process + * all these via ActionStack. + * * Save the IDE project, so the changes made by installers are persisted. + * * Generate config changes munge for plugin and apply it to all required files + * * Generate metadata for plugin and plugin modules and save it to 'cordova_plugins.js' + * + * @param {PluginInfo} plugin A PluginInfo structure representing plugin to install + * @param {Object} [options={}] An installation options. It is expected but is not necessary + * that options would contain 'variables' inner object with 'PACKAGE_NAME' field set by caller. + * + * @returns {Promise} Returns a Q promise, either resolved in case of success, rejected otherwise. + */ +PluginManager.prototype.doOperation = function (operation, plugin, options) { + if (operation !== PluginManager.INSTALL && operation !== PluginManager.UNINSTALL) { return Q.reject(new CordovaError('The parameter is incorrect. The opeation must be either "add" or "remove"')); } + + if (!plugin || plugin.constructor.name !== 'PluginInfo') { return Q.reject(new CordovaError('The parameter is incorrect. The first parameter should be a PluginInfo instance')); } + + // Set default to empty object to play safe when accesing properties + options = options || {}; + + var self = this; + var actions = new ActionStack(); + + // gather all files need to be handled during operation ... + plugin.getFilesAndFrameworks(this.platform, options) + .concat(plugin.getAssets(this.platform)) + .concat(plugin.getJsModules(this.platform)) + // ... put them into stack ... + .forEach(function (item) { + var installer = self.project.getInstaller(item.itemType); + var uninstaller = self.project.getUninstaller(item.itemType); + var actionArgs = [item, plugin, self.project, options]; + + var action; + if (operation === PluginManager.INSTALL) { + action = actions.createAction(installer, actionArgs, uninstaller, actionArgs); + } else /* op === PluginManager.UNINSTALL */{ + action = actions.createAction(uninstaller, actionArgs, installer, actionArgs); + } + actions.push(action); + }); + + // ... and run through the action stack + return actions.process(this.platform) + .then(function () { + if (self.project.write) { + self.project.write(); + } + + if (operation === PluginManager.INSTALL) { + // Ignore passed `is_top_level` option since platform itself doesn't know + // anything about managing dependencies - it's responsibility of caller. + self.munger.add_plugin_changes(plugin, options.variables, /* is_top_level= */true, /* should_increment= */true, options.force); + self.munger.platformJson.addPluginMetadata(plugin); + } else { + self.munger.remove_plugin_changes(plugin, /* is_top_level= */true); + self.munger.platformJson.removePluginMetadata(plugin); + } + + // Save everything (munge and plugin/modules metadata) + self.munger.save_all(); + + var metadata = self.munger.platformJson.generateMetadata(); + fs.writeFileSync(path.join(self.locations.www, 'cordova_plugins.js'), metadata, 'utf-8'); + + // CB-11022 save plugin metadata to both www and platform_www if options.usePlatformWww is specified + if (options.usePlatformWww) { + fs.writeFileSync(path.join(self.locations.platformWww, 'cordova_plugins.js'), metadata, 'utf-8'); + } + }); +}; + +PluginManager.prototype.addPlugin = function (plugin, installOptions) { + return this.doOperation(PluginManager.INSTALL, plugin, installOptions); +}; + +PluginManager.prototype.removePlugin = function (plugin, uninstallOptions) { + return this.doOperation(PluginManager.UNINSTALL, plugin, uninstallOptions); +}; diff --git a/node_modules/cordova-common/src/events.js b/node_modules/cordova-common/src/events.js new file mode 100644 index 0000000..d16485c --- /dev/null +++ b/node_modules/cordova-common/src/events.js @@ -0,0 +1,81 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +const EventEmitter = require('events').EventEmitter; + +const MAX_LISTENERS = 20; + +const INSTANCE_KEY = Symbol.for('org.apache.cordova.common.CordovaEvents'); + +let EVENTS_RECEIVER = null; + +class CordovaEventEmitter extends EventEmitter { + /** + * Sets up current instance to forward emitted events to another EventEmitter + * instance. + * + * @param {EventEmitter} [eventEmitter] The emitter instance to forward + * events to. Falsy value, when passed, disables forwarding. + */ + forwardEventsTo (eventEmitter) { + // If no argument is specified disable events forwarding + if (!eventEmitter) { + EVENTS_RECEIVER = undefined; + return; + } + + if (!(eventEmitter instanceof EventEmitter)) { + throw new Error('Cordova events can be redirected to another EventEmitter instance only'); + } + + // CB-10940 Skipping forwarding to self to avoid infinite recursion. + // This is the case when the modules are npm-linked. + if (this !== eventEmitter) { + EVENTS_RECEIVER = eventEmitter; + } else { + // Reset forwarding if we are subscribing to self + EVENTS_RECEIVER = undefined; + } + } + + /** + * Sets up current instance to forward emitted events to another EventEmitter + * instance. + * + * @param {EventEmitter} [eventEmitter] The emitter instance to forward + * events to. Falsy value, when passed, disables forwarding. + */ + emit (eventName, ...args) { + if (EVENTS_RECEIVER) { + EVENTS_RECEIVER.emit(eventName, ...args); + } + + return super.emit(eventName, ...args); + } +} + +// This singleton instance pattern is based on the ideas from +// https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/ +if (Object.getOwnPropertySymbols(global).indexOf(INSTANCE_KEY) === -1) { + const events = new CordovaEventEmitter(); + events.setMaxListeners(MAX_LISTENERS); + global[INSTANCE_KEY] = events; +} + +module.exports = global[INSTANCE_KEY]; diff --git a/node_modules/cordova-common/src/superspawn.js b/node_modules/cordova-common/src/superspawn.js new file mode 100644 index 0000000..7c999a1 --- /dev/null +++ b/node_modules/cordova-common/src/superspawn.js @@ -0,0 +1,155 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var crossSpawn = require('cross-spawn'); +var fs = require('fs-extra'); +var _ = require('underscore'); +var Q = require('q'); +var events = require('./events'); +var iswin32 = process.platform === 'win32'; + +/** + * A special implementation for child_process.spawn that handles + * Windows-specific issues with batch files and spaces in paths. Returns a + * promise that succeeds only for return code 0. It is also possible to + * subscribe on spawned process' stdout and stderr streams using progress + * handler for resultant promise. + * + * @example spawn('mycommand', [], {stdio: 'pipe'}) .progress(function (stdio){ + * if (stdio.stderr) { console.error(stdio.stderr); } }) + * .then(function(result){ // do other stuff }) + * + * @param {String} cmd A command to spawn + * @param {String[]} [args=[]] An array of arguments, passed to spawned + * process + * @param {Object} [opts={}] A configuration object + * @param {String|String[]|Object} opts.stdio Property that configures how + * spawned process' stdio will behave. Has the same meaning and possible + * values as 'stdio' options for child_process.spawn method + * (https://nodejs.org/api/child_process.html#child_process_options_stdio). + * @param {Object} [env={}] A map of extra environment variables + * @param {String} [cwd=process.cwd()] Working directory for the command + * @param {Boolean} [chmod=false] If truthy, will attempt to set the execute + * bit before executing on non-Windows platforms + * + * @return {Promise} A promise that is either fulfilled if the spawned + * process is exited with zero error code or rejected otherwise. If the + * 'stdio' option set to 'default' or 'pipe', the promise also emits progress + * messages with the following contents: + * { + * 'stdout': ..., + * 'stderr': ... + * } + */ +exports.spawn = function (cmd, args, opts) { + args = args || []; + opts = opts || {}; + var spawnOpts = {}; + var d = Q.defer(); + + if (opts.stdio !== 'default') { + // Ignore 'default' value for stdio because it corresponds to child_process's default 'pipe' option + spawnOpts.stdio = opts.stdio; + } + + if (opts.cwd) { + spawnOpts.cwd = opts.cwd; + } + + if (opts.env) { + spawnOpts.env = _.extend(_.extend({}, process.env), opts.env); + } + + if (opts.chmod && !iswin32) { + try { + // This fails when module is installed in a system directory (e.g. via sudo npm install) + fs.chmodSync(cmd, '755'); + } catch (e) { + // If the perms weren't set right, then this will come as an error upon execution. + } + } + + events.emit(opts.printCommand ? 'log' : 'verbose', 'Running command: ' + cmd + ' ' + args.join(' ')); + + // At least until Node.js 8, child_process.spawn will throw exceptions + // instead of emitting error events in certain cases (like EACCES), Thus we + // have to wrap the execution in try/catch to convert them into rejections. + try { + var child = crossSpawn.spawn(cmd, args, spawnOpts); + } catch (e) { + whenDone(e); + return d.promise; + } + var capturedOut = ''; + var capturedErr = ''; + + if (child.stdout) { + child.stdout.setEncoding('utf8'); + child.stdout.on('data', function (data) { + capturedOut += data; + d.notify({ stdout: data }); + }); + } + + if (child.stderr) { + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function (data) { + capturedErr += data; + d.notify({ stderr: data }); + }); + } + + child.on('close', whenDone); + child.on('error', whenDone); + function whenDone (arg) { + if (child) { + child.removeListener('close', whenDone); + child.removeListener('error', whenDone); + } + var code = typeof arg === 'number' ? arg : arg && arg.code; + + events.emit('verbose', 'Command finished with error code ' + code + ': ' + cmd + ' ' + args); + if (code === 0) { + d.resolve(capturedOut.trim()); + } else { + var errMsg = cmd + ': Command failed with exit code ' + code; + if (capturedErr) { + errMsg += ' Error output:\n' + capturedErr.trim(); + } + var err = new Error(errMsg); + if (capturedErr) { + err.stderr = capturedErr; + } + if (capturedOut) { + err.stdout = capturedOut; + } + err.code = code; + d.reject(err); + } + } + + return d.promise; +}; + +exports.maybeSpawn = function (cmd, args, opts) { + if (fs.existsSync(cmd)) { + return exports.spawn(cmd, args, opts); + } + return Q(null); +}; diff --git a/node_modules/cordova-common/src/util/addProperty.js b/node_modules/cordova-common/src/util/addProperty.js new file mode 100644 index 0000000..6e058d3 --- /dev/null +++ b/node_modules/cordova-common/src/util/addProperty.js @@ -0,0 +1,31 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +module.exports = function addProperty (module, property, modulePath, obj) { + obj = obj || module.exports; + // Add properties as getter to delay load the modules on first invocation + Object.defineProperty(obj, property, { + configurable: true, + get: function () { + var delayLoadedModule = module.require(modulePath); + obj[property] = delayLoadedModule; + return delayLoadedModule; + } + }); +}; diff --git a/node_modules/cordova-common/src/util/formatError.js b/node_modules/cordova-common/src/util/formatError.js new file mode 100644 index 0000000..c080ad2 --- /dev/null +++ b/node_modules/cordova-common/src/util/formatError.js @@ -0,0 +1,53 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +const CordovaError = require('../CordovaError/CordovaError'); + +/** + * Formats an error for logging. + * + * @param {Error} error - The error to be formatted. + * @param {boolean} isVerbose - Whether the include additional debugging + * information when formatting the error. + * + * @returns {string} The formatted error message. + */ +module.exports = function formatError (error, isVerbose) { + var message = ''; + + if (error instanceof CordovaError) { + message = error.toString(isVerbose); + } else if (error instanceof Error) { + if (isVerbose) { + message = error.stack; + } else { + message = error.message; + } + } else { + // Plain text error message + message = error; + } + + if (typeof message === 'string' && !message.toUpperCase().startsWith('ERROR:')) { + // Needed for backward compatibility with external tools + message = 'Error: ' + message; + } + + return message; +}; diff --git a/node_modules/cordova-common/src/util/plist-helpers.js b/node_modules/cordova-common/src/util/plist-helpers.js new file mode 100644 index 0000000..9e50dbf --- /dev/null +++ b/node_modules/cordova-common/src/util/plist-helpers.js @@ -0,0 +1,95 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +// contains PLIST utility functions +var __ = require('underscore'); +var plist = require('plist'); + +// adds node to doc at selector +module.exports.graftPLIST = graftPLIST; +function graftPLIST (doc, xml, selector) { + var obj = plist.parse('' + xml + ''); + + var node = doc[selector]; + if (node && Array.isArray(node) && Array.isArray(obj)) { + node = node.concat(obj); + for (var i = 0; i < node.length; i++) { + for (var j = i + 1; j < node.length; ++j) { + if (nodeEqual(node[i], node[j])) { node.splice(j--, 1); } + } + } + doc[selector] = node; + } else { + // plist uses objects for . If we have two dicts we merge them instead of + // overriding the old one. See CB-6472 + if (node && __.isObject(node) && __.isObject(obj) && !__.isDate(node) && !__.isDate(obj)) { // arrays checked above + __.extend(obj, node); + } + doc[selector] = obj; + } + + return true; +} + +// removes node from doc at selector +module.exports.prunePLIST = prunePLIST; +function prunePLIST (doc, xml, selector) { + var obj = plist.parse('' + xml + ''); + + pruneOBJECT(doc, selector, obj); + + return true; +} + +function pruneOBJECT (doc, selector, fragment) { + if (Array.isArray(fragment) && Array.isArray(doc[selector])) { + var empty = true; + for (var i in fragment) { + for (var j in doc[selector]) { + empty = pruneOBJECT(doc[selector], j, fragment[i]) && empty; + } + } + if (empty) { + delete doc[selector]; + return true; + } + } else if (nodeEqual(doc[selector], fragment)) { + delete doc[selector]; + return true; + } + + return false; +} + +function nodeEqual (node1, node2) { + if (typeof node1 !== typeof node2) { return false; } else if (typeof node1 === 'string') { + node2 = escapeRE(node2).replace(/\\\$\(\S+\)/gm, '(.*?)'); + return new RegExp('^' + node2 + '$').test(node1); + } else { + for (var key in node2) { + if (!nodeEqual(node1[key], node2[key])) return false; + } + return true; + } +} + +// escape string for use in regex +function escapeRE (str) { + return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); +} diff --git a/node_modules/cordova-common/src/util/xml-helpers.js b/node_modules/cordova-common/src/util/xml-helpers.js new file mode 100644 index 0000000..ad2fdb3 --- /dev/null +++ b/node_modules/cordova-common/src/util/xml-helpers.js @@ -0,0 +1,339 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +/** + * contains XML utility functions, some of which are specific to elementtree + */ + +var fs = require('fs-extra'); +var path = require('path'); +var _ = require('underscore'); +var et = require('elementtree'); +var stripBom = require('strip-bom'); + +var ROOT = /^\/([^/]*)/; +var ABSOLUTE = /^\/([^/]*)\/(.*)/; + +module.exports = { + // compare two et.XML nodes, see if they match + // compares tagName, text, attributes and children (recursively) + equalNodes: function (one, two) { + if (one.tag !== two.tag) { + return false; + } else if (one.text.trim() !== two.text.trim()) { + return false; + } else if (one._children.length !== two._children.length) { + return false; + } + + if (!attribMatch(one, two)) return false; + + for (var i = 0; i < one._children.length; i++) { + if (!module.exports.equalNodes(one._children[i], two._children[i])) { + return false; + } + } + + return true; + }, + + // adds node to doc at selector, creating parent if it doesn't exist + graftXML: function (doc, nodes, selector, after) { + var parent = module.exports.resolveParent(doc, selector); + if (!parent) { + // Try to create the parent recursively if necessary + try { + var parentToCreate = et.XML('<' + path.basename(selector) + '/>'); + var parentSelector = path.dirname(selector); + + this.graftXML(doc, [parentToCreate], parentSelector); + } catch (e) { + return false; + } + parent = module.exports.resolveParent(doc, selector); + if (!parent) return false; + } + + nodes.forEach(function (node) { + // check if child is unique first + if (uniqueChild(node, parent)) { + var children = parent.getchildren(); + var insertIdx = after ? findInsertIdx(children, after) : children.length; + + // TODO: replace with parent.insert after the bug in ElementTree is fixed + parent.getchildren().splice(insertIdx, 0, node); + } + }); + + return true; + }, + + // adds new attributes to doc at selector + // Will only merge if attribute has not been modified already or --force is used + graftXMLMerge: function (doc, nodes, selector, xml) { + var target = module.exports.resolveParent(doc, selector); + if (!target) return false; + + // saves the attributes of the original xml before making changes + xml.oldAttrib = _.extend({}, target.attrib); + + nodes.forEach(function (node) { + var attributes = node.attrib; + for (var attribute in attributes) { + target.attrib[attribute] = node.attrib[attribute]; + } + }); + + return true; + }, + + // overwrite all attributes to doc at selector with new attributes + // Will only overwrite if attribute has not been modified already or --force is used + graftXMLOverwrite: function (doc, nodes, selector, xml) { + var target = module.exports.resolveParent(doc, selector); + if (!target) return false; + + // saves the attributes of the original xml before making changes + xml.oldAttrib = _.extend({}, target.attrib); + + // remove old attributes from target + var targetAttributes = target.attrib; + for (var targetAttribute in targetAttributes) { + delete targetAttributes[targetAttribute]; + } + + // add new attributes to target + nodes.forEach(function (node) { + var attributes = node.attrib; + for (var attribute in attributes) { + target.attrib[attribute] = node.attrib[attribute]; + } + }); + + return true; + }, + + // removes node from doc at selector + pruneXML: function (doc, nodes, selector) { + var parent = module.exports.resolveParent(doc, selector); + if (!parent) return false; + + nodes.forEach(function (node) { + var matchingKid = findChild(node, parent); + if (matchingKid !== undefined) { + // stupid elementtree takes an index argument it doesn't use + // and does not conform to the python lib + parent.remove(matchingKid); + } + }); + + return true; + }, + + // restores attributes from doc at selector + pruneXMLRestore: function (doc, selector, xml) { + var target = module.exports.resolveParent(doc, selector); + if (!target) return false; + + if (xml.oldAttrib) { + target.attrib = _.extend({}, xml.oldAttrib); + } + + return true; + }, + + pruneXMLRemove: function (doc, selector, nodes) { + var target = module.exports.resolveParent(doc, selector); + if (!target) return false; + + nodes.forEach(function (node) { + var attributes = node.attrib; + for (var attribute in attributes) { + if (target.attrib[attribute]) { + delete target.attrib[attribute]; + } + } + }); + + return true; + }, + + parseElementtreeSync: function (filename) { + var contents = stripBom(fs.readFileSync(filename, 'utf-8')); + return new et.ElementTree(et.XML(contents)); + }, + + resolveParent: function (doc, selector) { + var parent, tagName, subSelector; + + // handle absolute selector (which elementtree doesn't like) + if (ROOT.test(selector)) { + tagName = selector.match(ROOT)[1]; + // test for wildcard "any-tag" root selector + if (tagName === '*' || tagName === doc._root.tag) { + parent = doc._root; + + // could be an absolute path, but not selecting the root + if (ABSOLUTE.test(selector)) { + subSelector = selector.match(ABSOLUTE)[2]; + parent = parent.find(subSelector); + } + } else { + return false; + } + } else { + parent = doc.find(selector); + } + return parent; + } +}; + +function findChild (node, parent) { + const matches = parent.findall(node.tag); + return matches.find(m => module.exports.equalNodes(node, m)); +} + +function uniqueChild (node, parent) { + return !findChild(node, parent); +} + +// Find the index at which to insert an entry. After is a ;-separated priority list +// of tags after which the insertion should be made. E.g. If we need to +// insert an element C, and the rule is that the order of children has to be +// As, Bs, Cs. After will be equal to "C;B;A". +function findInsertIdx (children, after) { + var childrenTags = children.map(function (child) { return child.tag; }); + var afters = after.split(';'); + var afterIndexes = afters.map(function (current) { return childrenTags.lastIndexOf(current); }); + var foundIndex = _.find(afterIndexes, function (index) { return index !== -1; }); + + // add to the beginning if no matching nodes are found + return typeof foundIndex === 'undefined' ? 0 : foundIndex + 1; +} + +var BLACKLIST = ['platform', 'feature', 'plugin', 'engine']; +var SINGLETONS = ['content', 'author', 'name']; +function mergeXml (src, dest, platform, clobber) { + // Do nothing for blacklisted tags. + if (BLACKLIST.includes(src.tag)) return; + + // Handle attributes + Object.getOwnPropertyNames(src.attrib).forEach(function (attribute) { + if (clobber || !dest.attrib[attribute]) { + dest.attrib[attribute] = src.attrib[attribute]; + } + }); + // Handle text + if (src.text && (clobber || !dest.text)) { + dest.text = src.text; + } + // Handle children + src.getchildren().forEach(mergeChild); + + // Handle platform + if (platform) { + src.findall('platform[@name="' + platform + '"]').forEach(function (platformElement) { + platformElement.getchildren().forEach(mergeChild); + }); + } + + // Handle duplicate preference tags (by name attribute) + removeDuplicatePreferences(dest); + + function mergeChild (srcChild) { + var srcTag = srcChild.tag; + var destChild = new et.Element(srcTag); + var foundChild; + var query = srcTag + ''; + var shouldMerge = true; + + if (BLACKLIST.includes(srcTag)) return; + + if (SINGLETONS.includes(srcTag)) { + foundChild = dest.find(query); + if (foundChild) { + destChild = foundChild; + dest.remove(destChild); + } + } else { + // Check for an exact match and if you find one don't add + var mergeCandidates = dest.findall(query) + .filter(function (foundChild) { + return foundChild && textMatch(srcChild, foundChild) && attribMatch(srcChild, foundChild); + }); + + if (mergeCandidates.length > 0) { + destChild = mergeCandidates[0]; + dest.remove(destChild); + shouldMerge = false; + } + } + + mergeXml(srcChild, destChild, platform, clobber && shouldMerge); + dest.append(destChild); + } + + function removeDuplicatePreferences (xml) { + // reduce preference tags to a hashtable to remove dupes + var prefHash = xml.findall('preference[@name][@value]').reduce(function (previousValue, currentValue) { + previousValue[currentValue.attrib.name] = currentValue.attrib.value; + return previousValue; + }, {}); + + // remove all preferences + xml.findall('preference[@name][@value]').forEach(function (pref) { + xml.remove(pref); + }); + + // write new preferences + Object.keys(prefHash).forEach(function (key) { + var element = et.SubElement(xml, 'preference'); + element.set('name', key); + element.set('value', this[key]); + }, prefHash); + } +} + +// Expose for testing. +module.exports.mergeXml = mergeXml; + +function textMatch (elm1, elm2) { + var text1 = elm1.text ? elm1.text.replace(/\s+/, '') : ''; + var text2 = elm2.text ? elm2.text.replace(/\s+/, '') : ''; + return (text1 === '' || text1 === text2); +} + +function attribMatch (one, two) { + var oneAttribKeys = Object.keys(one.attrib); + var twoAttribKeys = Object.keys(two.attrib); + + if (oneAttribKeys.length !== twoAttribKeys.length) { + return false; + } + + for (var i = 0; i < oneAttribKeys.length; i++) { + var attribName = oneAttribKeys[i]; + + if (one.attrib[attribName] !== two.attrib[attribName]) { + return false; + } + } + + return true; +} diff --git a/node_modules/cordova-plugin-googlemaps/.cordova b/node_modules/cordova-plugin-googlemaps/.cordova new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/cordova-plugin-googlemaps/.eslintignore b/node_modules/cordova-plugin-googlemaps/.eslintignore new file mode 100644 index 0000000..2113bd9 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.eslintignore @@ -0,0 +1,2 @@ +www/Promise.js +*.spec.js \ No newline at end of file diff --git a/node_modules/cordova-plugin-googlemaps/.eslintrc.js b/node_modules/cordova-plugin-googlemaps/.eslintrc.js new file mode 100644 index 0000000..bd081fe --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.eslintrc.js @@ -0,0 +1,42 @@ +module.exports = { + "env": { + "browser": true, + "node": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 5 + }, + "rules": { + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "always" + ], + "no-console": 0 + }, + "globals": { + "require": false, + "Symbol": false, + "window": false, + "cordova": false, + "Promise": false, + "module": false, + "angular": false, + "firebase": false, + "exports": false, + "google": false, + "plugin": false + } +}; diff --git a/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE.md b/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..12451b5 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,68 @@ + + +**I'm submitting a ...** (check one with "x") +- [ ] question +- [ ] any problem or bug report +- [ ] feature request + +**If you choose 'problem or bug report', please select OS:** (check one with "x") +- [ ] **Android** +- [ ] **iOS** + +**cordova information:** (run `$> cordova plugin list`) + +``` +insert the output from the command here +``` + +**If you use `@ionic-native/google-maps`, please tell the package.json (only `@ionic-native/core` and `@ionic-native/google-maps` are fine mostly)** + +**Current behavior:** + + +**Expected behavior:** + + +**Screen capture or video record:** + + +**Related code, data or error log (please format your code or data):** + +``` +The person who share your project files on Github (or other git repository) is in faster lane than other people. +Please share your project files on Github or others(Bitbucket, Gitlabs...etc). +If you don't want to share your project files, please create a demo project, then share it. + +Screen captures, and/or native logs(such as Logcat, xcode logs) are appreciate. + +Giving much information, you are waiting time is less. +Thank you for your cooperation. +``` diff --git a/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/feature_request.md b/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..478a05c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: Feature request +about: Suggest an idea for this plugin + +--- + + + +**Please tell us your new feature idea** + +**Why do you need it?** + +**How helps other people?** diff --git a/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/private-support.md b/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/private-support.md new file mode 100644 index 0000000..18262e7 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/private-support.md @@ -0,0 +1,41 @@ +--- +name: Private support +about: How to request private support? + +--- + +This plugin is open source project, thus you can use this plugin as free! + +In order to share the issue knowledges with other people, we would like to ask you to make converstations in open issue threads as much as possible. + +However some people, some companies do not allow to do that, because of their NDAs or privacy policies. + +Yes, we understand thier situations. + +In that case, choose **"private support"**. +If you choose "**private support**", we will make a private chat room for you, then we can discuss your issues. +We don't discover your code or your application screen captures into public spaces. + +### Fee + +You can choose one of following options: + +- only one time, ask only one issue. + Please donate $15 USD. + +- 1 month private support, up to 10 issues. + Please donate $100 USD. + +- one year private support, no cap. + Please donate $1,000 USD. + + + +### How to apply it? + +Please make an issue with this template, then donate the fee amount. + + https://github.com/mapsplugin/cordova-plugin-googlemaps#please-support-this-plugin-activity + +You don't need to discover your information at this issue thread. +After donation, we will contact the email address which is used for paypal payment. diff --git a/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/question-or-bug-report.md b/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/question-or-bug-report.md new file mode 100644 index 0000000..2de8f14 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.github/ISSUE_TEMPLATE/question-or-bug-report.md @@ -0,0 +1,83 @@ +--- +name: Question or bug report +about: Create a report to help us improve + +--- + + + +**I'm submitting a ...** (check one with "x") +- [ ] question +- [ ] any problem or bug report + +**OS:** (check one with "x") +- [ ] **Android** +- [ ] **iOS** +- [ ] **Browser** + +**cordova information:** (run `$> cordova plugin list`) + +``` +insert the output from the command here +``` + +**If you use `@ionic-native/google-maps`, please tell the package.json (only `@ionic-native/core` and `@ionic-native/google-maps` are fine mostly)** +``` +@ionic-native/core : +@ionic-native/google-maps : +``` + +**Current behavior:** + + +**Expected behavior:** + + +**Screen capture or video record:** + + +**Related code, data or error log (please format your code or data):** + +``` +Please share your project files on Github or others(Bitbucket, Gitlabs...etc). +If you don't want to share your project files, please create a demo project, then share it. + +Screen captures, and/or native logs(such as Logcat, xcode logs) are appreciate. + +Giving much information, you are waiting time is less. +Thank you for your cooperation. +``` + +**Support this plugin activity** + +I appreicate if you give me a beer :beer: from [here]( +https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=SQPLZJ672HJ9N&lc=US&item_name=Cordova%2dGoogleMaps%2dPlugin&amount=5%2e00¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted) diff --git a/node_modules/cordova-plugin-googlemaps/.jsbeautifyrc b/node_modules/cordova-plugin-googlemaps/.jsbeautifyrc new file mode 100644 index 0000000..aee5ec4 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.jsbeautifyrc @@ -0,0 +1,11 @@ +{ + "js": { + "indent_size": 2, + "indent_char": " ", + "indent_level": 0, + "indent_with_tabs": false, + "preserve_newlines": true, + "max_preserve_newlines": 2, + "jslint_happy": true + } +} diff --git a/node_modules/cordova-plugin-googlemaps/.jshintrc b/node_modules/cordova-plugin-googlemaps/.jshintrc new file mode 100644 index 0000000..0be1498 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 5 +} diff --git a/node_modules/cordova-plugin-googlemaps/.travis.yml b/node_modules/cordova-plugin-googlemaps/.travis.yml new file mode 100644 index 0000000..1cb3404 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/.travis.yml @@ -0,0 +1,98 @@ +sudo: false + +env: + global: + - NODE_VERSION="10.12.0" + - ANDROID_API="28" + - ANDROID_BUILD_TOOLS="28.0.3" + - CORDOVA_VERSION="9.0.0" + +notifications: + email: false + +before_cache: + # Do not cache a few Gradle files/directories (see https://docs.travis-ci.com/user/languages/java/#Caching) + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + +cache: + directories: + + # Gradle dependencies + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + + # Android directory + - $HOME/.android + + # NVM + - $HOME/.nvm + +matrix: + include: + - os: linux + env: CORDOVA_PLATFORM="browser" + language: node_js + node_js: '${NODE_VERSION}' + + - if: branch = master + os: osx + env: CORDOVA_PLATFORM="ios" + language: objective-c + cache: + - bundler + - cocoapods + node_js: '${NODE_VERSION}' + osx_image: xcode9.4 + before_script: + - exist=$(which ios-deploy); if [[ -z "${exist}" ]]; then + npm -g install ios-deploy > /dev/null; + fi + + - if: branch = master + env: CORDOVA_PLATFORM="android" + os: linux + node_js: '${NODE_VERSION}' + language: android + jdk: oraclejdk8 + android: + components: + - tools + - android-${ANDROID_API} + - platform-tools + # The BuildTools version used by your project + - build-tools-${ANDROID_BUILD_TOOLS} + # The SDK version used to compile your project + - android-${ANDROID_API} + # Additional components + - extra-android-m2repository + - extra-google-m2repository + - sys-img-x86-android-${ANDROID_API} + licenses: + - 'android-sdk-license-.+' + - '.+' + +install: + + - exist=$(which nvm); if [[ -z "${exist}" ]]; then + rm -rf ~/.nvm > /dev/null; + git clone https://github.com/creationix/nvm.git ~/.nvm > /dev/null; + (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags` > /dev/null); + source ~/.nvm/nvm.sh && nvm > /dev/null; + fi + - exist=$(nvm list | grep ${NODE_VERSION}); if [[ -z "${exist}" ]]; then + nvm install ${NODE_VERSION} > /dev/null; + fi + - nvm use ${NODE_VERSION} > /dev/null; + - node --version + - exist=$(which cordova); if [[ -z "${exist}" ]]; then + npm -g install cordova@${CORDOVA_VERSION} > /dev/null; + fi + - cordova --version + - exist=$(which cordova-paramedic); if [[ -z "${exist}" ]]; then + npm -g install https://github.com/apache/cordova-paramedic; + fi + - npm i > /dev/null + +script: + - /bin/bash build_test.sh diff --git a/node_modules/cordova-plugin-googlemaps/LICENSE b/node_modules/cordova-plugin-googlemaps/LICENSE new file mode 100644 index 0000000..adba9c2 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/LICENSE @@ -0,0 +1,206 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014 Masashi Katsumata + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Additional conditions: + As of the cordova-plugin-googlemaps version 2.2.5, following user is prohibited to use this plugin (additional fork, copy the code from this repo, anything). + - the member of `Bemyeye Holdings limited (https://github.com/BeMyEye)` + diff --git a/node_modules/cordova-plugin-googlemaps/README.md b/node_modules/cordova-plugin-googlemaps/README.md new file mode 100644 index 0000000..4d4d973 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/README.md @@ -0,0 +1,433 @@ +# Cordova GoogleMaps plugin for Android, iOS and Browser v2.7.1 + +| Download | Build test (multiple_maps branch)| +|----------|---------------------------| +| [![](https://img.shields.io/npm/dm/cordova-plugin-googlemaps.svg)](https://npm-stat.com/charts.html?package=cordova-plugin-googlemaps) |[![](https://travis-ci.org/mapsplugin/cordova-plugin-googlemaps.svg?branch=multiple_maps)](https://travis-ci.org/mapsplugin/cordova-plugin-googlemaps/branches) | + + This plugin displays Google Maps in your application. + This plugin uses these libraries for each platforms: + + - Android : [Google Maps Android API](https://developers.google.com/maps/documentation/android/) + - iOS : [Google Maps SDK for iOS](https://developers.google.com/maps/documentation/ios/) + - Browser : [Google Maps JavaScript API v3](https://developers.google.com/maps/documentation/javascript/) + + Both [PhoneGap](http://phonegap.com/) and [Apache Cordova](http://cordova.apache.org/) are supported. + + + + + + +
+

Android, iOS

+ +
+

Browser

+ +
+ +----- + +## Guides + + - [How to generate API keys?](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/api_key/README.md) + - [Hello, World](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/hello-world/README.md) + - [Hello, World (with PhoneGap Build)](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/hello-world-phonegap-build/README.md) + - [Trouble shootings](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/tree/master/troubleshootings/README.md) + +## Quick install + + ``` + $> cordova plugin add cordova-plugin-googlemaps + ``` + +Then set your Google Maps API keys into your `config.xml` (Android / iOS). + + ```xml + + + + + ``` + +For browser platform, + + ```js + // If your app runs this program on browser, + // you need to set `API_KEY_FOR_BROWSER_RELEASE` and `API_KEY_FOR_BROWSER_DEBUG` + // before `plugin.google.maps.Map.getMap()` + // + // API_KEY_FOR_BROWSER_RELEASE for `https:` protocol + // API_KEY_FOR_BROWSER_DEBUG for `http:` protocol + // + plugin.google.maps.environment.setEnv({ + 'API_KEY_FOR_BROWSER_RELEASE': '(YOUR_API_KEY_IS_HERE)', + 'API_KEY_FOR_BROWSER_DEBUG': '' // optional + }); + + // Create a Google Maps native view under the map_canvas div. + var map = plugin.google.maps.Map.getMap(div); + + ``` + +## PhoneGap Build settings + + ```xml + + + + + + ``` + +## Install optional variables (config.xml) + + - ![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/icon-android.png) **PLAY_SERVICES_VERSION = (15.0.1)**
+ The Google Play Services SDK version. + _You need to specify the same version number with all other plugins._ + Check out the latest version [here](https://developers.google.com/android/guides/releases). + + - ![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/icon-android.png) **ANDROID_SUPPORT_V4_VERSION = (27.1.1)**
+ This plugin requires the Android support library v4. + _The minimum version is 24.1.0._ + Check out the latest version [here](https://developer.android.com/topic/libraries/support-library/revisions.html). + + - ![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/icon-ios.png) **LOCATION_WHEN_IN_USE_DESCRIPTION**
+ This message is displayed when your application requests **LOCATION PERMISSION for only necessary times**. + + - ![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/icon-ios.png) **LOCATION_ALWAYS_USAGE_DESCRIPTION**
+ This message is displayed when your application requests **LOCATION PERMISSION for always**. + +--------------------------------------------------------------------------------------------------------- + +## Please support this plugin activity. + + In order to keep this plugin as free, please consider to donate little amount for this project. + + [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=SQPLZJ672HJ9N&lc=US&item_name=cordova%2dgooglemaps%2dplugin¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted) + +--------------------------------------------------------------------------------------------------------- + +## Release Notes + + - **v2.7.1** + - Fix: (iOS) UiWebView references present in v2.7.0 + + - **v2.7.0** + - Re-adoption: cordova-plugin-googlemaps-sdk dependency + - Important update: No longer support `UIWebView` on iOS. `WKWebView` only. + - Fix: (iOS) Can't load image files from local host on ionic 4 / 5 + - Update: (Android) prevent null pointer error in AsyncLoadImage.java + - Fix: Css animation interference when call setDiv and there is a push/pop page + - Fix: (Android/iOS/Browser) KML parser crash + - Fix: flickering and wrong rendering of some DOM elements + - Add: `map.stopAnimation()` + - Fix: can't remove map while map.animateCamera() is running + - Update: (Android) Increase cache memory size + - Update: (Android/iOS) Danish localization + - Fix: (Android) Prevent resize event after `map.setDiv(null)` + - Fix: (Android/iOS) Can not interactive with the map inside
+ - Fix: jslint errors + - Fix: marker.setIcon crashes + - Update: Set default value range to heading and tilt + - Fix: (Android/iOS) touch detection is wrong after clicking on back button very soon. + - Fix: An error occurs when you click a marker of marker cluster #2660 + - Remove promise-7.0.4-min.js.map + - Fix: (iOS) bug fix: App crashes if "bearing" property is "" + - Fix: HTMLColor2RGBA() converts to incorrect value + - Fix: (Android) Can't load marker image from the Internet + - many bug fixes... + + +--------------------------------------------------------------------------------------------------------- + +## Demos + + +[Demo (Browser)](https://mapsplugin.github.io/HelloGoogleMap/) + +![](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/raw/master/v1.4.0/top/demo.gif) + + +--------------------------------------------------------------------------------------------------------- + +## Documentation + +![](https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/documentations.png?raw=true) + +[All documentations are here!!](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md) + +https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md + +**Quick examples** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Map
+var options = {
+  camera: {
+    target: {lat: ..., lng: ...},
+    zoom: 19
+  }
+};
+var map = plugin.google.maps.Map.getMap(mapDiv, options)

Marker
+var marker = map.addMarker({
+  position: {lat: ..., lng: ...},
+  title: "Hello Cordova Google Maps for iOS and Android",
+  snippet: "This plugin is awesome!"
+})

MarkerCluster
+var markerCluster = map.addMarkerCluster({
+  //maxZoomLevel: 5,
+  boundsDraw: true,
+  markers: dummyData(),
+  icons: [
+      {min: 2, max: 100, url: "./img/blue.png", anchor: {x: 16, y: 16}},
+      {min: 100, max: 1000, url: "./img/yellow.png", anchor: {x: 16, y: 16}},
+      {min: 1000, max: 2000, url: "./img/purple.png", anchor: {x: 24, y: 24}},
+      {min: 2000, url: "./img/red.png",anchor: {x: 32,y: 32}}
+  ]
+});

HtmlInfoWindow
+var html = "<img src='./House-icon.png' width='64' height='64' >" +
+           "<br>" +
+           "This is an example";
+htmlInfoWindow.setContent(html);
+htmlInfoWindow.open(marker);
+

Circle
+var circle = map.addCircle({
+  'center': {lat: ..., lng: ...},
+  'radius': 300,
+  'strokeColor' : '#AA00FF',
+  'strokeWidth': 5,
+  'fillColor' : '#880000'
+});

Polyline
+var polyline = map.addPolyline({
+  points: AIR_PORTS,
+  'color' : '#AA00FF',
+  'width': 10,
+  'geodesic': true
+});

Polygon
+var polygon = map.addPolygon({
+  'points': GORYOKAKU_POINTS,
+  'strokeColor' : '#AA00FF',
+  'strokeWidth': 5,
+  'fillColor' : '#880000'
+});

GroundOverlay
+var groundOverlay = map.addGroundOverlay({
+  'url': "./newark_nj_1922.jpg",
+  'bounds': [
+    {"lat": 40.712216, "lng": -74.22655},
+    {"lat": 40.773941, "lng": -74.12544}
+  ],
+  'opacity': 0.5
+});
+

TileOverlay
+var tileOverlay = map.addTileOverlay({
+  debug: true,
+  opacity: 0.75,
+  getTile: function(x, y, zoom) {
+    return "../images/map-for-free/" + zoom + "_" + x + "-" + y + ".gif"
+  }
+});

KmlOverlay
+map.addKmlOverlay({
+  'url': 'polygon.kml'
+}, function(kmlOverlay) { ... });

Geocoder
+plugin.google.maps.Geocoder.geocode({
+  // US Capital cities
+  "address": [
+    "Montgomery, AL, USA", ... "Cheyenne, Wyoming, USA"
+  ]
+}, function(mvcArray) { ... });

poly utility
+var GORYOKAKU_POINTS = [
+  {lat: 41.79883, lng: 140.75675},
+  ...
+  {lat: 41.79883, lng: 140.75673}
+]
+var contain = plugin.google.maps.geometry.poly.containsLocation(
+                    position, GORYOKAKU_POINTS);
+marker.setIcon(contain ? "blue" : "red");
+

encode utility
+var GORYOKAKU_POINTS = [
+  {lat: 41.79883, lng: 140.75675},
+  ...
+  {lat: 41.79883, lng: 140.75673}
+]
+var encodedPath = plugin.google.maps.geometry.
+                       encoding.encodePath(GORYOKAKU_POINTS);
+

spherical utility
+var heading = plugin.google.maps.geometry.spherical.computeHeading(
+                        markerA.getPosition(), markerB.getPosition());
+label.innerText = "heading : " + heading.toFixed(0) + "°";
+

Location service
+plugin.google.maps.LocationService.getMyLocation(function(result) {
+  alert(["Your current location:\n",
+      "latitude:" + location.latLng.lat.toFixed(3),
+      "longitude:" + location.latLng.lng.toFixed(3),
+      "speed:" + location.speed,
+      "time:" + location.time,
+      "bearing:" + location.bearing].join("\n"));
+});
+

StreetView
+var div = document.getElementById("pano_canvas1");
+var panorama = plugin.google.maps.StreetView.getPanorama(div, {
+  camera: {
+    target: {lat: 42.345573, lng: -71.098326}
+  }
+});
+ + +--------------------------------------------------------------------------------------------------------- + +### What is the difference between this plugin and Google Maps JavaScript API v3? + +Google Maps JavaScript API v3 works on any platforms, +but it does not work if device is **offline**. + +This plugin uses three different APIs: +- Android : [Google Maps Android API](https://developers.google.com/maps/documentation/android/) +- iOS : [Google Maps SDK for iOS](https://developers.google.com/maps/documentation/ios/) +- Browser : [Google Maps JavaScript API v3](https://developers.google.com/maps/documentation/javascript/) + +In Android and iOS applications, this plugin displays native Google Maps views, which is **faster** than Google Maps JavaScript API v3. +And it even works if the device is **offline**. + +In Browser platform, this plugin displays JS map views (Google Maps JavaScript API v3). +It should work as PWA (progressive web application), but the device has to be **online**. + +In order to work for all platforms, this plugin provides **own API** instead of each original APIs. +You can write your code `similar to` the Google Maps JavaScript API v3. + +**Feature comparison table** + +| | Google Maps JavaScript API v3 | Cordova-Plugin-GoogleMaps(Android,iOS)| Cordova-Plugin-GoogleMaps(Browser) | +|----------------|-----------------------------------|---------------------------------------|---------------------------------------| +|Rendering system| JavaScript + HTML | JavaScript + Native API's | JavaScript | +|Offline map | Not possible | Possible (only your displayed area) | Not possible | +|3D View | Not possible | Possible | Not possible | +|Platform | All browsers | Android and iOS applications only | All browsers | +|Tile image | Bitmap | Vector | Bitmap | + +**Class comparison table** + +| Google Maps JavaScript API v3 | Cordova-Plugin-GoogleMaps | +|-----------------------------------|---------------------------------------| +| google.maps.Map | Map | +| google.maps.Marker | Marker | +| google.maps.InfoWindow | Default InfoWindow, and HtmlInfoWindow| +| google.maps.Circle | Circle | +| google.maps.Rectangle | Polygon | +| google.maps.Polyline | Polyline | +| google.maps.Polygon | Polygon | +| google.maps.GroundOverlay | GroundOverlay | +| google.maps.ImageMapType | TileOverlay | +| google.maps.MVCObject | BaseClass | +| google.maps.MVCArray | BaseArrayClass | +| google.maps.Geocoder | plugin.google.maps.geocoder | +| google.maps.geometry.spherical | plugin.google.maps.geometry.spherical | +| google.maps.geometry.encoding | plugin.google.maps.geometry.encoding | +| google.maps.geometry.poly | plugin.google.maps.geometry.poly | +| (not available) | MarkerCluster | +| google.maps.KmlLayer | KmlOverlay | +| (not available) | LocationService | +| google.maps.StreetView | StreetView :sparkles: | +| google.maps.Data | (not available) | +| google.maps.DirectionsService | (not available) | +| google.maps.DistanceMatrixService | (not available) | +| google.maps.TransitLayer | (not available) | +| google.maps.places.* | (not available) | +| google.maps.visualization.* | (not available) | + +### How does this plugin work (Android, iOS)? + +This plugin generates native map views, and puts them **under the browser**. + +The map views are not HTML elements. This means that they are not a `
` or anything HTML related. +But you can specify the size and position of the map view using its containing `
`. + +This plugin changes the background to `transparent` in your application. +Then the plugin detects your touch position, which is either meant for the `native map` or an `html element` +(which can be on top of your map, or anywhere else on the screen). + +![](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/raw/master/v1.4.0/class/Map/mechanism.png) + +The benefit of this plugin is the ability to automatically detect which HTML elements are over the map or not. + +For instance, in the image below, say you tap on the header div (which is over the map view). +The plugin will detect whether your tap is for the header div or for the map view and then pass the touch event appropriately. + +This means **you can use the native Google Maps views similar to HTML elements**. + +![](https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/touch.png) + + +--------------------------------------------------------------------------------------------------------- +## Official Communities + +- Gitter : (managed by @Hirbod) + + https://gitter.im/nightstomp/cordova-plugin-googlemaps diff --git a/node_modules/cordova-plugin-googlemaps/build_test.sh b/node_modules/cordova-plugin-googlemaps/build_test.sh new file mode 100644 index 0000000..cb36f67 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/build_test.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +DO_BUILD=0 + +# If pull request, do it. +echo "TRAVIS_EVENT_TYPE = ${TRAVIS_EVENT_TYPE}" +if [[ ${TRAVIS_EVENT_TYPE} == "pull_request" ]]; then + DO_BUILD=1 +fi + + +# If commit branch is master, do the build test +echo "TRAVIS_BRANCH = ${TRAVIS_BRANCH}" +if [[ ${TRAVIS_BRANCH} == "master" ]]; then + DO_BUILD=1 +fi + +if [ ${DO_BUILD} -eq 1 ]; then + cordova-paramedic --verbose --platform ${CORDOVA_PLATFORM} --justBuild --plugin ./tests; +fi + +if [[ ${CORDOVA_PLATFORM} == "browser" ]]; then + npm run test +fi diff --git a/node_modules/cordova-plugin-googlemaps/images/api_key_restrictions.png b/node_modules/cordova-plugin-googlemaps/images/api_key_restrictions.png new file mode 100644 index 0000000..bbdb4ed Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/api_key_restrictions.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/browser_demo.gif b/node_modules/cordova-plugin-googlemaps/images/browser_demo.gif new file mode 100644 index 0000000..613f1ff Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/browser_demo.gif differ diff --git a/node_modules/cordova-plugin-googlemaps/images/circle.png b/node_modules/cordova-plugin-googlemaps/images/circle.png new file mode 100644 index 0000000..1139967 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/circle.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/development_mode.png b/node_modules/cordova-plugin-googlemaps/images/development_mode.png new file mode 100644 index 0000000..27502b1 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/development_mode.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/documentations.png b/node_modules/cordova-plugin-googlemaps/images/documentations.png new file mode 100644 index 0000000..47a64ae Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/documentations.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/encode.png b/node_modules/cordova-plugin-googlemaps/images/encode.png new file mode 100644 index 0000000..4c195bf Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/encode.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/geocoder.png b/node_modules/cordova-plugin-googlemaps/images/geocoder.png new file mode 100644 index 0000000..56af079 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/geocoder.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/groundoverlay.png b/node_modules/cordova-plugin-googlemaps/images/groundoverlay.png new file mode 100644 index 0000000..65b6a1f Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/groundoverlay.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/htmlInfoWindow.png b/node_modules/cordova-plugin-googlemaps/images/htmlInfoWindow.png new file mode 100644 index 0000000..946548c Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/htmlInfoWindow.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/icon-android.png b/node_modules/cordova-plugin-googlemaps/images/icon-android.png new file mode 100644 index 0000000..895e823 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/icon-android.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/icon-ios.png b/node_modules/cordova-plugin-googlemaps/images/icon-ios.png new file mode 100644 index 0000000..c9d7a79 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/icon-ios.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/kmloverlay.png b/node_modules/cordova-plugin-googlemaps/images/kmloverlay.png new file mode 100644 index 0000000..78abfb1 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/kmloverlay.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/locationService.png b/node_modules/cordova-plugin-googlemaps/images/locationService.png new file mode 100644 index 0000000..1506b10 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/locationService.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/map.png b/node_modules/cordova-plugin-googlemaps/images/map.png new file mode 100644 index 0000000..189f9cd Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/map.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/marker.png b/node_modules/cordova-plugin-googlemaps/images/marker.png new file mode 100644 index 0000000..69038b7 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/marker.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/markercluster.png b/node_modules/cordova-plugin-googlemaps/images/markercluster.png new file mode 100644 index 0000000..3bc9bb8 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/markercluster.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/modern_browsers.png b/node_modules/cordova-plugin-googlemaps/images/modern_browsers.png new file mode 100644 index 0000000..cca0f7b Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/modern_browsers.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/poly.png b/node_modules/cordova-plugin-googlemaps/images/poly.png new file mode 100644 index 0000000..8d004d5 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/poly.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/polygon.png b/node_modules/cordova-plugin-googlemaps/images/polygon.png new file mode 100644 index 0000000..ca5106d Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/polygon.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/polyline.png b/node_modules/cordova-plugin-googlemaps/images/polyline.png new file mode 100644 index 0000000..c5d586a Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/polyline.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/spherical.png b/node_modules/cordova-plugin-googlemaps/images/spherical.png new file mode 100644 index 0000000..ad22cab Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/spherical.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/streetview.png b/node_modules/cordova-plugin-googlemaps/images/streetview.png new file mode 100644 index 0000000..55ca48f Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/streetview.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/tileoverlay.png b/node_modules/cordova-plugin-googlemaps/images/tileoverlay.png new file mode 100644 index 0000000..3ea80b3 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/tileoverlay.png differ diff --git a/node_modules/cordova-plugin-googlemaps/images/touch.png b/node_modules/cordova-plugin-googlemaps/images/touch.png new file mode 100644 index 0000000..7ad9414 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/images/touch.png differ diff --git a/node_modules/cordova-plugin-googlemaps/jest-cordova-resolver.js b/node_modules/cordova-plugin-googlemaps/jest-cordova-resolver.js new file mode 100644 index 0000000..ed7abe6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/jest-cordova-resolver.js @@ -0,0 +1,22 @@ +var Resolver = require('jest-resolve'); +var path = require('path'); +var fs = require('fs'); + +var pluginModuleRegex = /^cordova-plugin-googlemaps\.(.*)/; +var pathBrowser = './src/browser'; +var pathWww = './www'; + +module.exports = function(moduleId, options) { + if (pluginModuleRegex.test(moduleId)) { + var fileName = moduleId.match(pluginModuleRegex)[1] + '.js'; + if (fs.existsSync(path.resolve(pathBrowser, fileName))) { + return path.resolve(pathBrowser, fileName); + } + if (fs.existsSync(path.resolve(pathWww, fileName))) { + return path.resolve(pathWww, fileName); + } + + throw new Error(moduleId + 'could not be located'); + } + return Resolver.findNodeModule(moduleId, options); +}; diff --git a/node_modules/cordova-plugin-googlemaps/jest.config.js b/node_modules/cordova-plugin-googlemaps/jest.config.js new file mode 100644 index 0000000..2b73ad9 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleNameMapper: { + '^cordova/(.*)$': '/node_modules/cordova-js/src/common/$1', + }, + resolver: './jest-cordova-resolver.js', + globals: { + cordova: {}, + }, +}; diff --git a/node_modules/cordova-plugin-googlemaps/jsdoc.json b/node_modules/cordova-plugin-googlemaps/jsdoc.json new file mode 100644 index 0000000..2e33718 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/jsdoc.json @@ -0,0 +1,21 @@ +{ + "tags": { + "allowUnknownTags": false + }, + "source": { + "include": "www", + "includePattern": ".js$", + "excludePattern": "(node_modules/|docs|__test__)" + }, + "opts": { + "template": "node_modules/docdash/", + "encoding": "utf8", + "destination": "docs/", + "recurse": true, + "verbose": true + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false + } +} diff --git a/node_modules/cordova-plugin-googlemaps/package.json b/node_modules/cordova-plugin-googlemaps/package.json new file mode 100644 index 0000000..bc6c54e --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/package.json @@ -0,0 +1,65 @@ +{ + "name": "cordova-plugin-googlemaps", + "version": "2.7.1", + "description": "Google Maps native SDK for Android and iOS, and Google Maps JavaScript API v3 for browser.", + "cordova": { + "id": "cordova-plugin-googlemaps", + "platforms": [ + "android", + "ios", + "browser" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/mapsplugin/cordova-plugin-googlemaps.git" + }, + "keywords": [ + "google", + "maps", + "geo", + "google maps", + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "cordova-browser" + ], + "engines": [ + { + "name": "cordova-android", + "version": ">=5.1.0" + }, + { + "name": "cordova-ios", + "version": ">=5.1.1" + }, + { + "name": "apple-xcode", + "version": ">=10.0.0" + }, + { + "name": "apple-ios", + "version": ">=10.0.0" + } + ], + "scripts": { + "test": "jest && npm run eslint", + "jest": "jest", + "test:watch": "jest --watch", + "eslint": "node node_modules/eslint/bin/eslint www src" + }, + "author": "Masashi Katsumata, Hirbod Mirjavadi", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/mapsplugin/cordova-plugin-googlemaps/issues" + }, + "homepage": "https://github.com/mapsplugin/cordova-plugin-googlemaps", + "devDependencies": { + "cordova-js": "^4.2.4", + "eslint": "^5.7.0", + "jest": "^23.6.0" + }, + "dependencies": { + "cordova-common": "^3.1.0" + } +} diff --git a/node_modules/cordova-plugin-googlemaps/plugin.xml b/node_modules/cordova-plugin-googlemaps/plugin.xml new file mode 100644 index 0000000..1a2c801 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/plugin.xml @@ -0,0 +1,527 @@ + + + cordova-plugin-googlemaps + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + You can display native Google Maps views in cordova application. Native Google Maps view is faster, powerful, and more friendly for mobile applications. You can control the native Google Maps view through this plugin API. + + This plugin works in browser application as well. One code for Android, iOS, and browsers! + + https://github.com/mapsplugin/cordova-plugin-googlemaps.git + https://github.com/mapsplugin/cordova-plugin-googlemaps/issues + Masashi Katsumata, Hirbod + + Apache-2.0 + + google,maps,geo + + + + + + + + + + + + Official document https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md Please consider to buy a beer for us 🍺 https://github.com/mapsplugin/cordova-plugin-googlemaps#buy-us-a-beer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Official document https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md Please consider to buy a beer for us 🍺 https://github.com/mapsplugin/cordova-plugin-googlemaps#buy-us-a-beer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $LOCATION_WHEN_IN_USE_DESCRIPTION + + + + $LOCATION_ALWAYS_USAGE_DESCRIPTION + + + + + googlechromes + comgooglemaps + + + + + + + CFBundleTypeRole + Editor + CFBundleURLName + $PACKAGE_NAME + CFBundleURLSchemes + + $PACKAGE_NAME + + + + + + + + + + + + + + Official document https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md Please consider to buy a beer for us 🍺 https://github.com/mapsplugin/cordova-plugin-googlemaps#buy-us-a-beer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/cordova-plugin-googlemaps/pull_request_template.md b/node_modules/cordova-plugin-googlemaps/pull_request_template.md new file mode 100644 index 0000000..7361202 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/pull_request_template.md @@ -0,0 +1,9 @@ +# Pull request guide + +Thank you for considering to improve this cordova-plugin-googlemaps. + +When you create a pull request, please make it to **multiple_maps** branch instead of master branch. + +Because **the multiple_maps branch is edge version**. + +Thank you for your understanding. diff --git a/node_modules/cordova-plugin-googlemaps/src/android/frameworks/pgm-custom.gradle b/node_modules/cordova-plugin-googlemaps/src/android/frameworks/pgm-custom.gradle new file mode 100644 index 0000000..0144d22 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/frameworks/pgm-custom.gradle @@ -0,0 +1,106 @@ +def searchFileInParents(target) { + def searchPath = target + for (int i = 0; i < 8; i++) { + println(searchPath) + def targetFile = file(searchPath) + if(!targetFile.canRead()) { + searchPath = '../' + searchPath; + } else { + return targetFile; + } + } + return null; +} + +android { + // #1. Find (project)/config.xml file, + // because ionic capacitor does not copy nodes + def configXmlFile = searchFileInParents("config.xml") + + if (configXmlFile == null) { + // #2. Find (project)/app/src/main/res/xml/config.xml file, + // cordova should copy to this file + configXmlFile = searchFileInParents("app/src/main/res/xml/config.xml") + } + + if (configXmlFile == null) { + throw new GradleException("Can not find config.xml file.") + } + + // parse xml file + def widget = new XmlParser().parse(configXmlFile) + if (widget.preference == null) { + throw new GradleException("widget is null.") + } + if (widget.preference.size() == 0) { + throw new GradleException("Please add '' to the config.xml file.") + } + println("preference = " + widget.preference.size()) + Properties props = new Properties() + def isKeyFound = 0 + def useBetaSdk = 0 + props.setProperty("GOOGLE_MAPS_PLAY_SERVICES_VERSION", "16.0.0"); + props.setProperty("ANDROID_SUPPORT_V4_VERSION", "27.1.1"); + props.setProperty("GOOGLE_MAPS_ANDROID_SDK", ""); + + widget.preference.each { pref -> + def name = pref.attributes().name + def value = pref.attributes().value + println("name = " + name); + if (name == "GOOGLE_MAPS_ANDROID_API_KEY") { + isKeyFound = 1 + props.setProperty(name, value) + } + if (name == "GOOGLE_MAPS_PLAY_SERVICES_VERSION" || + name == "ANDROID_SUPPORT_V4_VERSION") { + props.setProperty(name, value) + } + if (name == "GOOGLE_MAPS_ANDROID_SDK") { + useBetaSdk = 1 + props.setProperty("GOOGLE_MAPS_ANDROID_SDK", value) + } + } + + + if (isKeyFound == 0) { + throw new GradleException("Please add '' to the config.xml file.") + } + + if (useBetaSdk == 1) { + def libsDirPath = System.getProperty("user.dir") + def libsDir = new File("${libsDirPath}/libs/") + if (!libsDir.exists()) { + libsDir.mkdirs() + } + + def mapsSDK = props.getProperty("GOOGLE_MAPS_ANDROID_SDK") + def f = new File("${libsDir.absolutePath}/${mapsSDK}.aar") + println("---->lib = " + f.absolutePath); + if (!f.exists()) { + new URL("https://dl.google.com/dl/geosdk/${mapsSDK}.aar").withInputStream{ i -> f.withOutputStream{ it << i }} + } + dependencies { + implementation name: mapsSDK, ext:'aar' + } + } + + def PLAY_SERVICES_VERSION = props.get("GOOGLE_MAPS_PLAY_SERVICES_VERSION") + def ANDROID_SUPPORT_V4_VERSION = props.get("ANDROID_SUPPORT_V4_VERSION") + println("---->PLAY_SERVICES_VERSION = ${PLAY_SERVICES_VERSION}") + println("---->ANDROID_SUPPORT_V4_VERSION = ${ANDROID_SUPPORT_V4_VERSION}") + dependencies { + implementation "com.google.android.gms:play-services-maps:${PLAY_SERVICES_VERSION}" + implementation "com.google.android.gms:play-services-location:${PLAY_SERVICES_VERSION}" + implementation "com.android.support:support-core-utils:${ANDROID_SUPPORT_V4_VERSION}" + } + + + buildTypes { + debug { + manifestPlaceholders.GOOGLE_MAPS_ANDROID_API_KEY = props.getProperty("GOOGLE_MAPS_ANDROID_API_KEY") + } + release { + manifestPlaceholders.GOOGLE_MAPS_ANDROID_API_KEY = props.getProperty("GOOGLE_MAPS_ANDROID_API_KEY") + } + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/frameworks/tbxml-android.aar b/node_modules/cordova-plugin-googlemaps/src/android/frameworks/tbxml-android.aar new file mode 100644 index 0000000..121a869 Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/src/android/frameworks/tbxml-android.aar differ diff --git a/node_modules/cordova-plugin-googlemaps/src/android/frameworks/tbxml-android.gradle b/node_modules/cordova-plugin-googlemaps/src/android/frameworks/tbxml-android.gradle new file mode 100644 index 0000000..d329088 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/frameworks/tbxml-android.gradle @@ -0,0 +1,22 @@ +repositories { + maven { url 'https://maven.google.com' } + mavenCentral() + jcenter() + def libsDirPath = System.getProperty("user.dir") + flatDir{ + dirs 'src/main/libs', 'libs', "${libsDirPath}/libs" + } +} + +dependencies { + implementation(name:'tbxml-android', ext:'aar') +} + +android { + compileSdkVersion 26 + + packagingOptions { + exclude 'README' + exclude 'LICENSE' + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/AsyncLoadImage.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/AsyncLoadImage.java new file mode 100644 index 0000000..16e0bb0 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/AsyncLoadImage.java @@ -0,0 +1,576 @@ +package plugin.google.maps; + +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.os.AsyncTask; +import android.util.Log; + +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaResourceApi; +import org.apache.cordova.CordovaWebView; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +public class AsyncLoadImage extends AsyncTask { + private AsyncLoadImageInterface callback; + private float density = Resources.getSystem().getDisplayMetrics().density; + private AsyncLoadImageOptions mOptions; + private String userAgent; + private String currentPageUrl; + + // Get max available VM memory, exceeding this amount will throw an + // OutOfMemory exception. + static int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); + // Use 1/8th of the available memory for this memory cache. + public static BitmapCache mIconCache = new BitmapCache(maxMemory / 8); + + private final String TAG = "AsyncLoadImage"; + private CordovaWebView webView; + private CordovaInterface cordova; + + public static class AsyncLoadImageOptions { + String url; + int width; + int height; + boolean noCaching; + } + + public static class AsyncLoadImageResult { + Bitmap image; + boolean cacheHit; + String cacheKey; + } + + public AsyncLoadImage(CordovaInterface cordova, CordovaWebView webView, AsyncLoadImageOptions options, AsyncLoadImageInterface callback) { + this.callback = callback; + this.mOptions = options; + this.webView = webView; + this.cordova = cordova; + } + + public static String getCacheKey(String url, int width, int height) { + if (url == null) { + return null; + } + try { + return getCacheKey(new URL(url), width, height); + } catch (MalformedURLException e) { + return url.hashCode() + "/" + width + "x" + height; + } + } + public static String getCacheKey(URL url, int width, int height) { + return url.hashCode() + "/" + width + "x" + height; + } + + public static void addBitmapToMemoryCache(String key, Bitmap image) { + if (getBitmapFromMemCache(key) == null) { + mIconCache.put(key, image.copy(image.getConfig(), true)); + } + } + + public static void removeBitmapFromMemCahce(String key) { + Bitmap image = mIconCache.remove(key); + if (image == null || image.isRecycled()) { + return; + } + image.recycle(); + } + + public static Bitmap getBitmapFromMemCache(String key) { + Bitmap image = mIconCache.get(key); + if (image == null || image.isRecycled()) { + return null; + } + + return image.copy(image.getConfig(), true); + } + + + @Override + protected void onCancelled(AsyncLoadImageResult result) { + super.onCancelled(result); + if (result == null) { + return; + } + + if (!result.image.isRecycled()) { + result.image.recycle(); + } + result.image = null; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + + //---------------------------------------------------------------- + // If icon url contains "cdvfile://", convert to physical path. + //---------------------------------------------------------------- + if (mOptions.url.indexOf("cdvfile://") == 0) { + CordovaResourceApi resourceApi = webView.getResourceApi(); + mOptions.url = PluginUtil.getAbsolutePathFromCDVFilePath(resourceApi, mOptions.url); + } + + String currentPage = webView.getUrl(); + if (currentPage == null) { + // Maybe someone close the map page. + this.cancel(true); + return; + } + currentPage = currentPage.replaceAll("#.*$", ""); + currentPage = currentPage.replaceAll("\\?.*$", ""); + currentPage = currentPage.replaceAll("[^\\/]*$", ""); + this.currentPageUrl = currentPage; + + //Log.d(TAG, "-->currentPageUrl = " + this.currentPageUrl); + + //View browserView = webView.getView(); + //String browserViewName = browserView.getClass().getName(); + this.userAgent = "Mozilla"; + + /* + if("org.xwalk.core.XWalkView".equals(browserViewName) || + "org.crosswalk.engine.XWalkCordovaView".equals(browserViewName)) { + + CordovaPreferences preferences = webView.getPreferences(); + // Set xwalk webview settings by Cordova preferences. + String xwalkUserAgent = preferences == null ? "" : preferences.getString("xwalkUserAgent", ""); + if (!xwalkUserAgent.isEmpty()) { + this.userAgent = xwalkUserAgent; + } + + String appendUserAgent = preferences.getString("AppendUserAgent", ""); + if (!appendUserAgent.isEmpty()) { + this.userAgent = this.userAgent + " " + appendUserAgent; + } + if ("".equals(this.userAgent)) { + this.userAgent = "Mozilla"; + } + } else { + this.userAgent = ((WebView) webView.getEngine().getView()).getSettings().getUserAgentString(); + } + */ + + } + + protected AsyncLoadImageResult doInBackground(Void... params) { + + int mWidth = mOptions.width; + int mHeight = mOptions.height; + String iconUrl = mOptions.url; + String orgIconUrl = iconUrl; + Bitmap image = null; + + if (iconUrl == null) { + return null; + } + + String cacheKey = null; + cacheKey = getCacheKey(orgIconUrl, mWidth, mHeight); + + image = getBitmapFromMemCache(cacheKey); + if (image != null) { + AsyncLoadImageResult result = new AsyncLoadImageResult(); + result.image = image; + result.cacheHit = true; + result.cacheKey = cacheKey; + return result; + } + +// Log.d(TAG, String.format("---->iconURL = %s", iconUrl)); + //-------------------------------- + // Load image from local path + //-------------------------------- + if (!iconUrl.startsWith("data:image")) { + + if (iconUrl.startsWith("http://localhost") || + iconUrl.startsWith("http://127.0.0.1")) { +// Log.d(TAG, String.format("---->(201)iconURL = %s", iconUrl)); + if (iconUrl.contains("://")) { + iconUrl = iconUrl.replaceAll("http://.+?/", "file:///android_asset/www/"); + } else { + // Avoid WebViewLocalServer (because can not make a connection for some reason) + iconUrl = "file:///android_asset/www/".concat(iconUrl); + } + } + + if (!iconUrl.contains("://") && + !iconUrl.startsWith("/") && + !iconUrl.startsWith("www/") && + !iconUrl.startsWith("data:image") && + !iconUrl.startsWith("./") && + !iconUrl.startsWith("../")) { + iconUrl = "./" + iconUrl; + //Log.d(TAG, "--> iconUrl = " + iconUrl); + } + + if (iconUrl.startsWith("./") || iconUrl.startsWith("../")) { + iconUrl = iconUrl.replace("(\\.\\/)+", "./"); + String currentPage = this.currentPageUrl; + currentPage = currentPage.replaceAll("[^\\/]*$", ""); + currentPage = currentPage.replaceAll("#.*$", ""); + currentPage = currentPage.replaceAll("\\/[^\\/]+\\.[^\\/]+$", ""); + if (!currentPage.endsWith("/")) { + currentPage = currentPage + "/"; + } + iconUrl = currentPage + iconUrl; + iconUrl = iconUrl.replaceAll("(\\/\\.\\/+)+", "/"); + //Log.d(TAG, "--> iconUrl = " + iconUrl); + } + + if (iconUrl.indexOf("file://") == 0 && + !iconUrl.contains("file:///android_asset/")) { + iconUrl = iconUrl.replace("file://", ""); + } else { + //Log.d(TAG, "--> iconUrl = " + iconUrl); + +// if (iconUrl.indexOf("file:///android_asset/") == 0) { +// iconUrl = iconUrl.replace("file:///android_asset/", ""); +// } + + //Log.d(TAG, "iconUrl(222) = " + iconUrl); + if (iconUrl.contains("./")) { + try { + boolean isAbsolutePath = iconUrl.startsWith("/"); + File relativePath = new File(iconUrl); + iconUrl = relativePath.getCanonicalPath(); + //Log.d(TAG, "iconUrl = " + iconUrl); + if (!isAbsolutePath) { + iconUrl = iconUrl.substring(1); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + cacheKey = getCacheKey(iconUrl, mWidth, mHeight); + + image = getBitmapFromMemCache(cacheKey); + if (image != null) { + AsyncLoadImageResult result = new AsyncLoadImageResult(); + result.image = image; + result.cacheHit = true; + result.cacheKey = cacheKey; + return result; + } + + cacheKey = getCacheKey(orgIconUrl, mWidth, mHeight); + + if (iconUrl.indexOf("http") == 0) { + //-------------------------------- + // Load image from the Internet + //-------------------------------- + try { + URL url = new URL(iconUrl); + + boolean redirect = true; + HttpURLConnection http = null; + String cookies = null; + int redirectCnt = 0; + while(redirect && redirectCnt < 10) { + redirect = false; + http = (HttpURLConnection)url.openConnection(); + http.setRequestMethod("GET"); + if (cookies != null) { + http.setRequestProperty("Cookie", cookies); + } + http.addRequestProperty("Accept-Language", "en-US,en;q=0.8"); + http.addRequestProperty("User-Agent", this.userAgent); + http.setInstanceFollowRedirects(true); + HttpURLConnection.setFollowRedirects(true); + + // normally, 3xx is redirect + try { + int status = http.getResponseCode(); + if (status != HttpURLConnection.HTTP_OK) { + if (status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_SEE_OTHER) + redirect = true; + } + if (redirect) { + // get redirect url from "location" header field + url = new URL(http.getHeaderField("Location")); + + // get the cookie if need, for login + cookies = http.getHeaderField("Set-Cookie"); + + // Disconnect the current connection + http.disconnect(); + redirectCnt++; + continue; + } + if (status == HttpURLConnection.HTTP_OK) { + break; + } else { + return null; + } + } catch (Exception e) { + Log.e(TAG, "can not connect to " + iconUrl, e); + } + } + + + Bitmap myBitmap = null; + InputStream inputStream = http.getInputStream(); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[16384]; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + inputStream.close(); + byte[] imageBytes = buffer.toByteArray(); + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + options.inPreferredConfig = Config.ARGB_8888; + + // The below line just checking the bitmap size (width,height). + // Returned value is always null. + BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options); + + if (mWidth < 1 && mHeight < 1) { + mWidth = options.outWidth; + mHeight = options.outHeight; + } + + // Resize + int newWidth = (int)(mWidth * density); + int newHeight = (int)(mHeight * density); + if (newWidth > 2000 || newHeight > 2000) { + float rationResize; + if (newWidth >= newHeight) { + rationResize = 2000.0f / ((float) newWidth); + } else { + rationResize = 2000.0f / ((float) newHeight); + } + newWidth = (int)(((float)newWidth) * rationResize); + newHeight = (int)(((float)newHeight) * rationResize); + Log.w(TAG, "Since the image size is too large, the image size resizes down mandatory"); + } + + + /** + * http://stackoverflow.com/questions/4821488/bad-image-quality-after-resizing-scaling-bitmap#7468636 + */ + Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888); + + float ratioX = newWidth / (float) options.outWidth; + float ratioY = newHeight / (float) options.outHeight; + float middleX = newWidth / 2.0f; + float middleY = newHeight / 2.0f; + + options.inJustDecodeBounds = false; + //options.inSampleSize = (int) Math.max(ratioX, ratioY); + options.outWidth = newWidth; + options.outHeight = newHeight; + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + + myBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options); + canvas.drawBitmap(myBitmap, middleX - options.outWidth / 2, middleY - options.outHeight / 2, new Paint(Paint.FILTER_BITMAP_FLAG)); + myBitmap.recycle(); + myBitmap = null; + canvas = null; + imageBytes = null; + + AsyncLoadImageResult result = new AsyncLoadImageResult(); + result.image = scaledBitmap; + result.cacheHit = false; + if (!mOptions.noCaching) { + result.cacheKey = cacheKey; + addBitmapToMemoryCache(cacheKey, scaledBitmap); + } + + return result; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + + } else { + //Log.d(TAG, "--> iconUrl = " + iconUrl); + if (iconUrl.indexOf("data:image/") == 0 && iconUrl.contains(";base64,")) { + String[] tmp = iconUrl.split(","); + image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]); + } else { + try { + InputStream inputStream = null; + if (iconUrl.startsWith("file:/android_asset/")) { + AssetManager assetManager = cordova.getActivity().getAssets(); + iconUrl = iconUrl.replace("file:/android_asset/", ""); + inputStream = assetManager.open(iconUrl); + //Log.d(TAG, "--> iconUrl = " + iconUrl); + } else if (iconUrl.startsWith("file:///android_asset/")) { + AssetManager assetManager = cordova.getActivity().getAssets(); + iconUrl = iconUrl.replace("file:///android_asset/", ""); + inputStream = assetManager.open(iconUrl); + //Log.d(TAG, "--> iconUrl = " + iconUrl); + } else if (iconUrl.startsWith("/")) { + File file = new File(iconUrl); + inputStream = new FileInputStream(file); + } + if (inputStream != null) { + image = BitmapFactory.decodeStream(inputStream); + inputStream.close(); + } else { + Log.e(TAG, "Can not load the file from '" + iconUrl + "'"); + return null; + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + /* + //-------------------------------- + // Load image from local path + //-------------------------------- + if (!iconUrl.contains("://") && + !iconUrl.startsWith("/") && + !iconUrl.startsWith("www/") && + !iconUrl.startsWith("data:image") && + !iconUrl.startsWith("./") && + !iconUrl.startsWith("../")) { + iconUrl = "./" + iconUrl; + Log.d(TAG, "--> iconUrl = " + iconUrl); + } + + if (iconUrl.startsWith("./") || iconUrl.startsWith("../")) { + iconUrl = iconUrl.replace("(\\.\\/)+", "./"); + String currentPage = this.currentPageUrl; + currentPage = currentPage.replaceAll("[^\\/]*$", ""); + currentPage = currentPage.replaceAll("#.*$", ""); + currentPage = currentPage.replaceAll("\\/[^\\/]+\\.[^\\/]+$", ""); + if (!currentPage.endsWith("/")) { + currentPage = currentPage + "/"; + } + iconUrl = currentPage + iconUrl; + iconUrl = iconUrl.replaceAll("(\\/\\.\\/+)+", "/"); + Log.d(TAG, "--> iconUrl = " + iconUrl); + } + + if (iconUrl.indexOf("data:image/") == 0 && iconUrl.contains(";base64,")) { + cacheKey = getCacheKey(iconUrl, mWidth, mHeight); + + image = getBitmapFromMemCache(cacheKey); + if (image != null) { + AsyncLoadImageResult result = new AsyncLoadImageResult(); + result.image = image; + result.cacheHit = true; + result.cacheKey = cacheKey; + return result; + } + + String[] tmp = iconUrl.split(","); + image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]); + } else if (iconUrl.indexOf("file://") == 0 && + !iconUrl.contains("file:///android_asset/")) { + iconUrl = iconUrl.replace("file://", ""); + File tmp = new File(iconUrl); + if (tmp.exists()) { + cacheKey = getCacheKey(iconUrl, mWidth, mHeight); + + image = getBitmapFromMemCache(cacheKey); + if (image != null) { + AsyncLoadImageResult result = new AsyncLoadImageResult(); + result.image = image; + result.cacheHit = true; + result.cacheKey = cacheKey; + return result; + } + + image = BitmapFactory.decodeFile(iconUrl); + } else { + //if (PluginMarker.this.mapCtrl.mPluginLayout.isDebug) { + Log.w(TAG, "icon is not found (" + iconUrl + ")"); + //} + return null; + } + } else { + Log.d(TAG, "--> iconUrl = " + iconUrl); + cacheKey = getCacheKey(iconUrl, mWidth, mHeight); + image = getBitmapFromMemCache(cacheKey); + + if (image != null) { + AsyncLoadImageResult result = new AsyncLoadImageResult(); + result.image = image; + result.cacheHit = true; + result.cacheKey = cacheKey; + return result; + } + + Log.d(TAG, "iconUrl = " + iconUrl); + if (iconUrl.indexOf("file:///android_asset/") == 0) { + iconUrl = iconUrl.replace("file:///android_asset/", ""); + } + + Log.d(TAG, "iconUrl = " + iconUrl); + if (iconUrl.contains("./")) { + try { + boolean isAbsolutePath = iconUrl.startsWith("/"); + File relativePath = new File(iconUrl); + iconUrl = relativePath.getCanonicalPath(); + Log.d(TAG, "iconUrl = " + iconUrl); + if (!isAbsolutePath) { + iconUrl = iconUrl.substring(1); + } + Log.d(TAG, "iconUrl = " + iconUrl); + } catch (Exception e) { + e.printStackTrace(); + } + } + */ + + //} + + if (mWidth > 0 && mHeight > 0) { + mWidth = Math.round(mWidth * density); + mHeight = Math.round(mHeight * density); + image = PluginUtil.resizeBitmap(image, mWidth, mHeight); + } else { + image = PluginUtil.scaleBitmapForDevice(image); + } + + AsyncLoadImageResult result = new AsyncLoadImageResult(); + result.image = image; + result.cacheHit = false; + if (!mOptions.noCaching) { + result.cacheKey = cacheKey; + addBitmapToMemoryCache(cacheKey, image); + } + + return result; + } + } + + + @Override + protected void onPostExecute(AsyncLoadImageResult result) { + this.callback.onPostExecute(result); + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/AsyncLoadImageInterface.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/AsyncLoadImageInterface.java new file mode 100644 index 0000000..62c58c0 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/AsyncLoadImageInterface.java @@ -0,0 +1,5 @@ +package plugin.google.maps; + +public interface AsyncLoadImageInterface { + public void onPostExecute(AsyncLoadImage.AsyncLoadImageResult result) ; +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/BitmapCache.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/BitmapCache.java new file mode 100644 index 0000000..fe3a7f4 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/BitmapCache.java @@ -0,0 +1,26 @@ +package plugin.google.maps; + +import android.graphics.Bitmap; +import android.util.LruCache; + +public class BitmapCache extends LruCache { + + public BitmapCache(int maxSize) { + super(maxSize); + } + + @Override + protected int sizeOf(String key, Bitmap bitmap) { + // The cache size will be measured in kilobytes rather than + // number of items. + return bitmap.getByteCount() / 1024; + } + + @Override + protected void entryRemoved(boolean evicted, String key, Bitmap oldBitmap, Bitmap newBitmap) { + if (!oldBitmap.isRecycled()) { + oldBitmap.recycle(); + oldBitmap = null; + } + } +} \ No newline at end of file diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/CordovaGoogleMaps.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/CordovaGoogleMaps.java new file mode 100644 index 0000000..7e965dc --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/CordovaGoogleMaps.java @@ -0,0 +1,584 @@ +package plugin.google.maps; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.RectF; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GooglePlayServicesUtil; +import com.google.android.gms.maps.MapsInitializer; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginEntry; +import org.apache.cordova.PluginManager; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Set; + +@SuppressWarnings("deprecation") +public class CordovaGoogleMaps extends CordovaPlugin implements ViewTreeObserver.OnScrollChangedListener{ + private final String TAG = "GoogleMapsPlugin"; + private Activity activity; + public ViewGroup root; + public MyPluginLayout mPluginLayout = null; + public boolean initialized = false; + public PluginManager pluginManager; + private static final Object timerLock = new Object(); + + @SuppressLint("NewApi") @Override + public void initialize(final CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + if (root != null) { + return; + } + LOG.setLogLevel(LOG.ERROR); + + activity = cordova.getActivity(); + final View view = webView.getView(); + view.getViewTreeObserver().addOnScrollChangedListener(CordovaGoogleMaps.this); + root = (ViewGroup) view.getParent(); + + pluginManager = webView.getPluginManager(); + + cordova.getActivity().runOnUiThread(new Runnable() { + @SuppressLint("NewApi") + public void run() { + + // Enable this, webView makes draw cache on the Android action bar issue. + //View view = webView.getView(); + //if (Build.VERSION.SDK_INT >= 21 || "org.xwalk.core.XWalkView".equals(view.getClass().getName())){ + // view.setLayerType(View.LAYER_TYPE_HARDWARE, null); + // Log.d("Layout", "--> view =" + view.isHardwareAccelerated()); //always false + //} + + + // ------------------------------ + // Check of Google Play Services + // ------------------------------ + int checkGooglePlayServices = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity); + + Log.d(TAG, "----> checkGooglePlayServices = " + (ConnectionResult.SUCCESS == checkGooglePlayServices)); + + if (checkGooglePlayServices != ConnectionResult.SUCCESS) { + // google play services is missing!!!! + /* + * Returns status code indicating whether there was an error. Can be one + * of following in ConnectionResult: SUCCESS, SERVICE_MISSING, + * SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID. + */ + Log.e(TAG, "---Google Play Services is not available: " + GooglePlayServicesUtil.getErrorString(checkGooglePlayServices)); + + boolean isNeedToUpdate = false; + + String errorMsg = PluginUtil.getPgmStrings(activity, "pgm_google_play_error"); + switch (checkGooglePlayServices) { + case ConnectionResult.DEVELOPER_ERROR: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_developer_error"); + break; + case ConnectionResult.INTERNAL_ERROR: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_internal_error"); + break; + case ConnectionResult.INVALID_ACCOUNT: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_invalid_account"); + break; + case ConnectionResult.LICENSE_CHECK_FAILED: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_license_check_failed"); + break; + case ConnectionResult.NETWORK_ERROR: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_network_error"); + break; + case ConnectionResult.SERVICE_DISABLED: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_service_disabled"); + break; + case ConnectionResult.SERVICE_INVALID: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_service_invalid"); + isNeedToUpdate = true; + break; + case ConnectionResult.SERVICE_MISSING: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_service_missing"); + isNeedToUpdate = true; + break; + case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_service_update_required"); + isNeedToUpdate = true; + break; + case ConnectionResult.SIGN_IN_REQUIRED: + errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_sign_in_required"); + break; + default: + isNeedToUpdate = true; + break; + } + + final boolean finalIsNeedToUpdate = isNeedToUpdate; + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); + alertDialogBuilder + .setMessage(errorMsg) + .setCancelable(false) + .setPositiveButton(PluginUtil.getPgmStrings(activity,"pgm_google_close_button"), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog,int id) { + dialog.dismiss(); + if (finalIsNeedToUpdate) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.gms"))); + } catch (android.content.ActivityNotFoundException anfe) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=com.google.android.gms"))); + } + } + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + + // show it + alertDialog.show(); + + Log.e(TAG, "Google Play Services is not available."); + return; + } + + webView.getView().setBackgroundColor(Color.TRANSPARENT); + webView.getView().setOverScrollMode(View.OVER_SCROLL_NEVER); + mPluginLayout = new MyPluginLayout(webView, activity); + mPluginLayout.stopTimer(); + + + // Check the API key + ApplicationInfo appliInfo = null; + try { + appliInfo = activity.getPackageManager().getApplicationInfo(activity.getPackageName(), PackageManager.GET_META_DATA); + } catch (NameNotFoundException e) {} + + String API_KEY = appliInfo.metaData.getString("com.google.android.maps.v2.API_KEY"); + if ("API_KEY_FOR_ANDROID".equals(API_KEY)) { + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); + + alertDialogBuilder + .setMessage(PluginUtil.getPgmStrings(activity,"pgm_api_key_error")) + .setCancelable(false) + .setPositiveButton(PluginUtil.getPgmStrings(activity,"pgm_google_close_button"), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog,int id) { + dialog.dismiss(); + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + + // show it + alertDialog.show(); + } + + + //------------------------------ + // Initialize Google Maps SDK + //------------------------------ + if (!initialized) { + try { + MapsInitializer.initialize(cordova.getActivity()); + initialized = true; + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + }); + + + } + + @Override + public boolean onOverrideUrlLoading(String url) { + mPluginLayout.stopTimer(); + /* + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + webView.loadUrl("javascript:if(window.cordova){cordova.fireDocumentEvent('plugin_url_changed', {});}"); + } + }); + */ + return false; + } + + + @Override + public void onScrollChanged() { + if (mPluginLayout == null) { + return; + } + View view = webView.getView(); + int scrollX = view.getScrollX(); + int scrollY = view.getScrollY(); + mPluginLayout.scrollTo(scrollX, scrollY); + + } + + @Override + public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + cordova.getThreadPool().submit(new Runnable() { + @Override + public void run() { + try { + if (action.equals("putHtmlElements")) { + CordovaGoogleMaps.this.putHtmlElements(args, callbackContext); + } else if ("clearHtmlElements".equals(action)) { + CordovaGoogleMaps.this.clearHtmlElements(args, callbackContext); + } else if ("pause".equals(action)) { + CordovaGoogleMaps.this.pause(args, callbackContext); + } else if ("resume".equals(action)) { + CordovaGoogleMaps.this.resume(args, callbackContext); + } else if ("getMap".equals(action)) { + CordovaGoogleMaps.this.getMap(args, callbackContext); + } else if ("getPanorama".equals(action)) { + CordovaGoogleMaps.this.getPanorama(args, callbackContext); + } else if ("removeMap".equals(action)) { + CordovaGoogleMaps.this.removeMap(args, callbackContext); + } else if ("backHistory".equals(action)) { + CordovaGoogleMaps.this.backHistory(args, callbackContext); + } else if ("updateMapPositionOnly".equals(action)) { + CordovaGoogleMaps.this.updateMapPositionOnly(args, callbackContext); + } + + } catch (JSONException e) { + e.printStackTrace(); + } + } + }); + return true; + + } + + public void updateMapPositionOnly(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final JSONObject elements = args.getJSONObject(0); + + Bundle elementsBundle = PluginUtil.Json2Bundle(elements); + float zoomScale = Resources.getSystem().getDisplayMetrics().density; + + Iterator domIDs = elementsBundle.keySet().iterator(); + String domId; + Bundle domInfo, size, currentDomInfo; + while (domIDs.hasNext()) { + domId = domIDs.next(); + domInfo = elementsBundle.getBundle(domId); + + size = domInfo.getBundle("size"); + RectF rectF = new RectF(); + rectF.left = (float)(Double.parseDouble(size.get("left") + "") * zoomScale); + rectF.top = (float)(Double.parseDouble(size.get("top") + "") * zoomScale); + rectF.right = rectF.left + (float)(Double.parseDouble(size.get("width") + "") * zoomScale); + rectF.bottom = rectF.top + (float)(Double.parseDouble(size.get("height") + "") * zoomScale); + + mPluginLayout.HTMLNodeRectFs.put(domId, rectF); + } + + if (mPluginLayout.isSuspended) { + mPluginLayout.updateMapPositions(); + } + callbackContext.success(); + } + public void backHistory(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + if (!webView.backHistory()) { + // If no more history back, exit the app + cordova.getActivity().finish(); + } + } + }); + } + + + + public synchronized void pause(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + synchronized (timerLock) { + if (mPluginLayout == null) { + callbackContext.success(); + return; + } + mPluginLayout.stopTimer(); + callbackContext.success(); + } + } + public synchronized void resume(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + synchronized (timerLock) { + if (mPluginLayout == null) { + callbackContext.success(); + return; + } + if (mPluginLayout.isSuspended) { + mPluginLayout.startTimer(); + } + callbackContext.success(); + + //On resume reapply background because it might have been changed by some other plugin + webView.getView().setBackgroundColor(Color.TRANSPARENT); + } + } + public void clearHtmlElements(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + if (mPluginLayout == null) { + callbackContext.success(); + return; + } + mPluginLayout.clearHtmlElements(); + callbackContext.success(); + } + public void putHtmlElements(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + final JSONObject elements = args.getJSONObject(0); + if (mPluginLayout == null) { + callbackContext.success(); + return; + } + + //Log.d(TAG, "--->stopFlag = " + mPluginLayout.stopFlag + ", mPluginLayout.needUpdatePosition = " + mPluginLayout.needUpdatePosition); + if (!mPluginLayout.stopFlag || mPluginLayout.needUpdatePosition) { + mPluginLayout.putHTMLElements(elements); + } + + //mPluginLayout.updateMapPositions(); + //mPluginLayout.startTimer(); + callbackContext.success(); + } + + @Override + public void onReset() { + super.onReset(); + if (mPluginLayout == null || mPluginLayout.pluginOverlays == null) { + return; + } + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + mPluginLayout.setBackgroundColor(Color.WHITE); + + Set mapIds = mPluginLayout.pluginOverlays.keySet(); + IPluginView pluginOverlay; + + // prevent the ConcurrentModificationException error. + String[] mapIdArray= mapIds.toArray(new String[mapIds.size()]); + for (String mapId : mapIdArray) { + if (mPluginLayout.pluginOverlays.containsKey(mapId)) { + pluginOverlay = mPluginLayout.removePluginOverlay(mapId); + pluginOverlay.remove(null, null); + pluginOverlay.onDestroy(); + mPluginLayout.HTMLNodes.remove(mapId); + } + } + mPluginLayout.HTMLNodes.clear(); + mPluginLayout.pluginOverlays.clear(); + + System.gc(); + Runtime.getRuntime().gc(); + } + }); + + } + + public void removeMap(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String mapId = args.getString(0); + if (mPluginLayout.pluginOverlays.containsKey(mapId)) { + IPluginView pluginOverlay = mPluginLayout.removePluginOverlay(mapId); + if (pluginOverlay != null) { + pluginOverlay.remove(null, null); + pluginOverlay.onDestroy(); + mPluginLayout.HTMLNodes.remove(mapId); + pluginOverlay = null; + } + + try { + Field pluginMapField = pluginManager.getClass().getDeclaredField("pluginMap"); + pluginMapField.setAccessible(true); + LinkedHashMap pluginMapInstance = (LinkedHashMap) pluginMapField.get(pluginManager); + pluginMapInstance.remove(mapId); + Field entryMapField = pluginManager.getClass().getDeclaredField("entryMap"); + entryMapField.setAccessible(true); + LinkedHashMap entryMapInstance = (LinkedHashMap) entryMapField.get(pluginManager); + entryMapInstance.remove(mapId); + } catch (Exception e) { + e.printStackTrace(); + } + + + } + + + + System.gc(); + Runtime.getRuntime().gc(); + callbackContext.success(); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void getMap(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + //------------------------------------------ + // Create an instance of PluginMap class. + //------------------------------------------ + JSONObject meta = args.getJSONObject(0); + String mapId = meta.getString("__pgmId"); + PluginMap pluginMap = new PluginMap(); + pluginMap.privateInitialize(mapId, cordova, webView, null); + pluginMap.initialize(cordova, webView); + pluginMap.mapCtrl = CordovaGoogleMaps.this; + pluginMap.self = pluginMap; + + PluginEntry pluginEntry = new PluginEntry(mapId, pluginMap); + pluginManager.addService(pluginEntry); + + pluginMap.getMap(args, callbackContext); + } + + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void getPanorama(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + //------------------------------------------ + // Create an instance of PluginStreetView class. + //------------------------------------------ + JSONObject meta = args.getJSONObject(0); + String mapId = meta.getString("__pgmId"); + Log.d(TAG, "---> mapId = " + mapId); + PluginStreetViewPanorama pluginStreetView = new PluginStreetViewPanorama(); + pluginStreetView.privateInitialize(mapId, cordova, webView, null); + pluginStreetView.initialize(cordova, webView); + pluginStreetView.mapCtrl = CordovaGoogleMaps.this; + pluginStreetView.self = pluginStreetView; + + PluginEntry pluginEntry = new PluginEntry(mapId, pluginStreetView); + pluginManager.addService(pluginEntry); + + pluginStreetView.getPanorama(args, callbackContext); + } + + @Override + public void onStart() { + super.onStart(); + + CollectionpluginEntries = pluginManager.getPluginEntries(); + for (PluginEntry pluginEntry: pluginEntries) { + if (pluginEntry.service.startsWith("map_")) { + pluginEntry.plugin.onStart(); + } + } + + } + @Override + public void onStop() { + super.onStop(); + + CollectionpluginEntries = pluginManager.getPluginEntries(); + for (PluginEntry pluginEntry: pluginEntries) { + if (pluginEntry.service.startsWith("map_")) { + pluginEntry.plugin.onStop(); + } + } + + } + @Override + public void onPause(boolean multitasking) { + super.onPause(multitasking); + if (mPluginLayout != null) { + mPluginLayout.stopTimer(); + } + + CollectionpluginEntries = pluginManager.getPluginEntries(); + for (PluginEntry pluginEntry: pluginEntries) { + if (pluginEntry.service.startsWith("map_")) { + pluginEntry.plugin.onPause(multitasking); + } + } + + } + + @Override + public void onResume(boolean multitasking) { + CollectionpluginEntries = pluginManager.getPluginEntries(); + for (Iterator iterator = pluginEntries.iterator(); iterator.hasNext();) { + PluginEntry pluginEntry = iterator.next(); + if (pluginEntry.service.startsWith("map_")) { + pluginEntry.plugin.onResume(multitasking); + } + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + + CollectionpluginEntries = pluginManager.getPluginEntries(); + for (PluginEntry pluginEntry: pluginEntries) { + if (pluginEntry.service.startsWith("map_")) { + pluginEntry.plugin.onDestroy(); + } + } + + } + + /** + * Called by the system when the device configuration changes while your activity is running. + * + * @param newConfig The new device configuration + */ + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + PluginMap pluginMap; + Collection collection = pluginManager.getPluginEntries(); + for (PluginEntry entry: collection) { + if ("plugin.google.maps.PluginMap".equals(entry.pluginClass) && entry.plugin != null) { + pluginMap = (PluginMap)entry.plugin; + if (pluginMap.map != null) { + + // Trigger the CAMERA_MOVE_END mandatory + pluginMap.onCameraIdle(); + } + } + } + } + }, 500); + /* + // Checks the orientation of the screen + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + Toast.makeText(activity, "landscape", Toast.LENGTH_SHORT).show(); + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ + Toast.makeText(activity, "portrait", Toast.LENGTH_SHORT).show(); + } + */ + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/IPluginView.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/IPluginView.java new file mode 100644 index 0000000..9ec0a79 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/IPluginView.java @@ -0,0 +1,24 @@ +package plugin.google.maps; + +import android.view.View; +import android.view.ViewGroup; + +import org.apache.cordova.CallbackContext; +import org.json.JSONArray; + +public interface IPluginView { + boolean getVisible(); + boolean getClickable(); + String getDivId(); + String getOverlayId(); + ViewGroup getView(); + int getViewDepth(); + void onDestroy(); + void onStart(); + void onStop(); + void onPause(boolean multitasking); + void onResume(boolean multitasking); + void remove(JSONArray args, final CallbackContext callbackContext); + void attachToWebView(JSONArray args, final CallbackContext callbackContext); + void detachFromWebView(JSONArray args, final CallbackContext callbackContext); +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPlugin.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPlugin.java new file mode 100644 index 0000000..9af3faa --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPlugin.java @@ -0,0 +1,244 @@ +package plugin.google.maps; + +import android.annotation.SuppressLint; +import android.content.res.Resources; +import android.util.Log; + +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.Circle; +import com.google.android.gms.maps.model.GroundOverlay; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.Polygon; +import com.google.android.gms.maps.model.Polyline; +import com.google.android.gms.maps.model.TileOverlay; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginEntry; +import org.json.JSONArray; +import org.json.JSONException; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class MyPlugin extends CordovaPlugin implements MyPluginInterface { + public MyPlugin self = null; + public final Map methods = new ConcurrentHashMap(); + protected static ExecutorService executorService = null; + + public CordovaGoogleMaps mapCtrl = null; + public GoogleMap map = null; + public PluginMap pluginMap = null; + protected boolean isRemoved = false; + protected static float density = Resources.getSystem().getDisplayMetrics().density; + + public void setPluginMap(PluginMap pluginMap) { + this.pluginMap = pluginMap; + this.mapCtrl = pluginMap.mapCtrl; + this.map = pluginMap.map; + } + protected String TAG = ""; + + @SuppressLint("UseSparseArrays") + @Override + public void initialize(CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + TAG = this.getServiceName(); + if (executorService == null) { + cordova.getThreadPool().execute(new Runnable() { + @Override + public void run() { + executorService = Executors.newCachedThreadPool(); + } + }); + } + } + @Override + public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) { + self = this; + executorService.submit(new Runnable() { + @Override + public void run() { + + if (isRemoved) { + // Ignore every execute calls. + return; + } + + synchronized (methods) { + if (methods.size() == 0) { + TAG = MyPlugin.this.getServiceName(); + if (!TAG.contains("-")) { + if (TAG.startsWith("map")) { + mapCtrl.mPluginLayout.pluginOverlays.put(TAG, (PluginMap) MyPlugin.this); + } else if (TAG.startsWith("streetview")) { + mapCtrl.mPluginLayout.pluginOverlays.put(TAG, (PluginStreetViewPanorama) MyPlugin.this); + } + } else { + PluginEntry pluginEntry = new PluginEntry(TAG, MyPlugin.this); + pluginMap.plugins.put(TAG, pluginEntry); + } + + + //CordovaPlugin plugin = mapCtrl.webView.getPluginManager().getPlugin(this.getServiceName()); + // Log.d("MyPlugin", "---> this = " + this); + // Log.d("MyPlugin", "---> plugin = " + plugin); + + Method[] classMethods = self.getClass().getMethods(); + for (Method classMethod : classMethods) { + methods.put(classMethod.getName(), classMethod); + } + } + + + // this.create(args, callbackContext); + // return true; + if (methods.containsKey(action)) { + if (self.mapCtrl.mPluginLayout.isDebug) { + try { + if (args != null && args.length() > 0) { + Log.d(TAG, "(debug)action=" + action + " args[0]=" + args.getString(0)); + } else { + Log.d(TAG, "(debug)action=" + action); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + Method method = methods.get(action); + try { + if (isRemoved) { + // Ignore every execute calls. + return; + } + method.invoke(self, args, callbackContext); + } catch (IllegalAccessException e) { + e.printStackTrace(); + callbackContext.error("Cannot access to the '" + action + "' method."); + } catch (InvocationTargetException e) { + e.printStackTrace(); + callbackContext.error("Cannot access to the '" + action + "' method."); + } + } + } + } + }); + return true; + + } + + + protected void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + // dummy + } + + protected synchronized Circle getCircle(String id) { + if (!pluginMap.objects.containsKey(id)) { + //Log.e(TAG, "---> can not find the circle : " + id); + return null; + } + return (Circle)pluginMap.objects.get(id); + } + protected synchronized GroundOverlay getGroundOverlay(String id) { + if (!pluginMap.objects.containsKey(id)) { + //Log.e(TAG, "---> can not find the ground overlay : " + id); + return null; + } + return (GroundOverlay)pluginMap.objects.get(id); + } + protected synchronized Marker getMarker(String id) { + if (!pluginMap.objects.containsKey(id)) { + //Log.e(TAG, "---> can not find the maker : " + id); + return null; + } + return (Marker)pluginMap.objects.get(id); + } + protected synchronized Polyline getPolyline(String id) { + if (!pluginMap.objects.containsKey(id)) { + //Log.e(TAG, "---> can not find the polyline : " + id); + return null; + } + return (Polyline)pluginMap.objects.get(id); + } + protected synchronized Polygon getPolygon(String id) { + if (!pluginMap.objects.containsKey(id)) { + //Log.e(TAG, "---> can not find the polygon : " + id); + return null; + } + return (Polygon)pluginMap.objects.get(id); + } + protected synchronized TileOverlay getTileOverlay(String id) { + if (!pluginMap.objects.containsKey(id)) { + //Log.e(TAG, "---> can not find the tileoverlay : " + id); + return null; + } + return (TileOverlay)pluginMap.objects.get(id); + } + + protected void setInt(String methodName, String id, int value, final CallbackContext callbackContext) throws JSONException { + this.setValue(methodName, int.class, id, value, callbackContext); + } + protected void setFloat(String methodName, String id, float value, final CallbackContext callbackContext) throws JSONException { + this.setValue(methodName, float.class, id, value, callbackContext); + } + protected void setDouble(String methodName, String id, float value, final CallbackContext callbackContext) throws JSONException { + this.setValue(methodName, double.class, id, value, callbackContext); + } + protected void setString(String methodName, String id, String value, final CallbackContext callbackContext) throws JSONException { + this.setValue(methodName, String.class, id, value, callbackContext); + } + + protected void setBoolean(String methodName, String id, Boolean value, final CallbackContext callbackContext) throws JSONException { + this.setValue(methodName, boolean.class, id, value, callbackContext); + } + + private void setValue(String methodName, Class methodClass, String id, final Object value, final CallbackContext callbackContext) throws JSONException { + if (!pluginMap.objects.containsKey(id)) { + return; + } + final Object object = pluginMap.objects.get(id); + try { + final Method method = object.getClass().getDeclaredMethod(methodName, methodClass); + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + method.invoke(object, value); + callbackContext.success(); + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error(e.getMessage()); + } + } + }); + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error(e.getMessage()); + } + } + protected void clear() { + String[] keys = pluginMap.objects.keys.toArray(new String[pluginMap.objects.size()]); + Object object; + for (String key : keys) { + object = pluginMap.objects.remove(key); + object = null; + } + pluginMap.objects.clear(); + } + + + protected void onOverlayEvent(String eventName, String overlayId, LatLng point) { + webView.loadUrl("javascript:plugin.google.maps.Map." + + "_onOverlayEvent(" + + "'" + eventName + "','" + overlayId + "', " + + "new window.plugin.google.maps.LatLng(" + point.latitude + "," + point.longitude + ")" + + ")"); + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPluginInterface.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPluginInterface.java new file mode 100644 index 0000000..1900164 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPluginInterface.java @@ -0,0 +1,16 @@ +package plugin.google.maps; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaArgs; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaWebView; +import org.json.JSONArray; +import org.json.JSONException; + +public interface MyPluginInterface { + public void initialize(CordovaInterface cordova, CordovaWebView webView); + public void setPluginMap(PluginMap pluginMap); + public boolean execute(String action, String rawArgs, CallbackContext callbackContext) throws JSONException; + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException; + public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException; +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPluginLayout.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPluginLayout.java new file mode 100644 index 0000000..044f84d --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/MyPluginLayout.java @@ -0,0 +1,716 @@ +package plugin.google.maps; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.RectF; +import android.os.Bundle; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.AbsoluteLayout; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +import org.apache.cordova.CordovaWebView; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; + +@SuppressWarnings("deprecation") +public class MyPluginLayout extends FrameLayout implements ViewTreeObserver.OnScrollChangedListener, ViewTreeObserver.OnGlobalLayoutListener { + private static final String TAG = "MyPluginLayout"; + private View browserView; + private Context context; + private FrontLayerLayout frontLayer; + private ScrollView scrollView; + public FrameLayout scrollFrameLayout; + public Map pluginOverlays = new ConcurrentHashMap(); + private Map touchableWrappers = new ConcurrentHashMap(); + private boolean isScrolling = false; + public boolean isDebug = false; + public final Object _lockHtmlNodes = new Object(); + public Map CACHE_FIND_DOM = new ConcurrentHashMap(); + public Map HTMLNodes = new ConcurrentHashMap(); + public Map HTMLNodeRectFs = new ConcurrentHashMap(); + private Activity mActivity = null; + private Paint debugPaint = new Paint(); + public boolean stopFlag = false; + public boolean needUpdatePosition = false; + public boolean isSuspended = false; + private float zoomScale; + private static final Object timerLock = new Object(); + + private static Timer redrawTimer; + + @Override + public void onGlobalLayout() { + ViewTreeObserver observer = browserView.getViewTreeObserver(); + observer.removeGlobalOnLayoutListener(this); + observer.addOnScrollChangedListener(this); + } + + private Runnable resizeWorker = new Runnable() { + @Override + public void run() { + final int scrollY = browserView.getScrollY(); + final int scrollX = browserView.getScrollX(); + final int webviewWidth = browserView.getWidth(); + final int webviewHeight = browserView.getHeight(); + + Set keySet = pluginOverlays.keySet(); + String[] toArrayBuf = new String[pluginOverlays.size()]; + String[] mapIds = keySet.toArray(toArrayBuf); + String mapId; + IPluginView pluginOverlay; + RectF drawRect; + for (int i = 0; i < mapIds.length; i++) { + mapId = mapIds[i]; + if (mapId == null) { + continue; + } + pluginOverlay = pluginOverlays.get(mapId); + if (pluginOverlay == null || pluginOverlay.getDivId() == null) { + continue; + } + drawRect = HTMLNodeRectFs.get(pluginOverlay.getDivId()); + if (drawRect == null || drawRect.left == 0 && drawRect.top == 0 && drawRect.width() == 0 && drawRect.height() == 0) { + + continue; + } + + int width = (int)drawRect.width(); + int height = (int)drawRect.height(); + int x = (int) drawRect.left; + int y = (int) drawRect.top + scrollY; + + + + if (pluginOverlay.getVisible()) { + if (pluginOverlay.getOverlayId().startsWith("streetview_")) { + if (y + height >= scrollY && + x + width >= scrollX && + y < scrollY + webviewHeight && + x < scrollX + webviewWidth) { + MyPluginLayout.this.addPluginOverlay(pluginOverlay); + } else { + + scrollFrameLayout.removeView(pluginOverlay.getView()); + pluginOverlay.getView().removeView(touchableWrappers.remove(pluginOverlay.getOverlayId())); + } + } + } + + ViewGroup.LayoutParams lParams = pluginOverlay.getView().getLayoutParams(); + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) lParams; + //Log.d("MyPluginLayout", "-->FrameLayout x = " + x + ", y = " + y + ", w = " + width + ", h = " + height); + if (params.leftMargin == x && params.topMargin == y && + params.width == width && params.height == height) { + continue; + } + params.width = width; + params.height = height; + params.leftMargin = x; + params.topMargin = y; + pluginOverlay.getView().setLayoutParams(params); + + + } + } + }; + + public void updateMapPositions() { + mActivity.runOnUiThread(resizeWorker); + } + + private class ResizeTask extends TimerTask { + @Override + public void run() { + mActivity.runOnUiThread(resizeWorker); + } + } + + @SuppressLint("NewApi") + public MyPluginLayout(CordovaWebView webView, Activity activity) { + super(webView.getView().getContext()); + this.browserView = webView.getView(); + browserView.getViewTreeObserver().addOnGlobalLayoutListener(this); + mActivity = activity; + ViewGroup root = (ViewGroup) browserView.getParent(); + this.context = browserView.getContext(); + //if (Build.VERSION.SDK_INT >= 21 || "org.xwalk.core.XWalkView".equals(browserView.getClass().getName())) { + // browserView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + //} + + zoomScale = Resources.getSystem().getDisplayMetrics().density; + Log.e(TAG, "--> zoomScale = " + zoomScale); + frontLayer = new FrontLayerLayout(this.context); + + scrollView = new ScrollView(this.context); + scrollView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + + root.removeView(browserView); + frontLayer.addView(browserView); + + scrollFrameLayout = new FrameLayout(this.context); + scrollFrameLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + + + View dummyView = new View(this.context); + dummyView.setLayoutParams(new LayoutParams(1, 99999)); + scrollFrameLayout.addView(dummyView); + + scrollView.setHorizontalScrollBarEnabled(true); + scrollView.setVerticalScrollBarEnabled(true); + scrollView.addView(scrollFrameLayout); + + browserView.setDrawingCacheEnabled(false); + + + this.addView(scrollView); + this.addView(frontLayer); + root.addView(this); + browserView.setBackgroundColor(Color.TRANSPARENT); + /* + if("org.xwalk.core.XWalkView".equals(browserView.getClass().getName()) + || "org.crosswalk.engine.XWalkCordovaView".equals(browserView.getClass().getName())) { + try { + // view.setZOrderOnTop(true) + // Called just in time as with root.setBackground(...) the color + // come in front and take the whole screen + browserView.getClass().getMethod("setZOrderOnTop", boolean.class) + .invoke(browserView, true); + } + catch(Exception e) { + e.printStackTrace(); + } + } + */ + scrollView.setHorizontalScrollBarEnabled(false); + scrollView.setVerticalScrollBarEnabled(false); + startTimer(); + } + + public synchronized void stopTimer() { + synchronized (timerLock) { + try { + if (redrawTimer != null) { + redrawTimer.cancel(); + redrawTimer.purge(); + ResizeTask task = new ResizeTask(); + task.run(); + isSuspended = true; + } + } catch (Exception e) { + e.printStackTrace(); + } + redrawTimer = null; + } + } + + public synchronized void startTimer() { + synchronized (timerLock) { + if (redrawTimer != null) { + return; + } + + redrawTimer = new Timer(); + redrawTimer.scheduleAtFixedRate(new ResizeTask(), 0, 25); + //mActivity.getWindow().getDecorView().requestFocus(); <--- This changes thread number sometimes. Do not use this! + isSuspended = false; + } + } + + + + public void clearHtmlElements() { + synchronized (_lockHtmlNodes) { + String[] keys = HTMLNodes.keySet().toArray(new String[HTMLNodes.size()]); + for (int i = 0; i < HTMLNodes.size(); i++) { + HTMLNodes.remove(keys[i]); + HTMLNodeRectFs.remove(keys[i]); + } + } + } + + public void putHTMLElements(JSONObject elements) { + + + Map newBuffer = new ConcurrentHashMap(); + Map newBufferRectFs = new ConcurrentHashMap(); + + Bundle elementsBundle = PluginUtil.Json2Bundle(elements); + + Iterator domIDs = elementsBundle.keySet().iterator(); + String domId; + Bundle domInfo, size; + while (domIDs.hasNext()) { + domId = domIDs.next(); + domInfo = elementsBundle.getBundle(domId); + if (domInfo == null) { + continue; + } + + size = domInfo.getBundle("size"); + if (size == null) { + continue; + } + + RectF rectF = new RectF(); + rectF.left = (float)(Double.parseDouble(size.get("left") + "") * zoomScale); + rectF.top = (float)(Double.parseDouble(size.get("top") + "") * zoomScale); + rectF.right = (float)(Double.parseDouble(size.get("right") + "") * zoomScale); + rectF.bottom = (float)(Double.parseDouble(size.get("bottom") + "") * zoomScale); + + + newBufferRectFs.put(domId, rectF); + + domInfo.remove("size"); + newBuffer.put(domId, domInfo); + } + + Map oldBuffer = HTMLNodes; + Map oldBufferRectFs = HTMLNodeRectFs; + + synchronized (_lockHtmlNodes) { + HTMLNodes = newBuffer; + HTMLNodeRectFs = newBufferRectFs; + } + + String[] keys = oldBuffer.keySet().toArray(new String[oldBuffer.size()]); + for (int i = 0; i < oldBuffer.size(); i++) { + oldBuffer.remove(keys[i]); + oldBufferRectFs.remove(keys[i]); + } + } + + public void setDebug(final boolean debug) { + this.isDebug = debug; + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (debug) { + inValidate(); + } + } + }); + } + + public IPluginView removePluginOverlay(final String overlayId) { + if (pluginOverlays == null || !pluginOverlays.containsKey(overlayId)) { + return null; + } + final IPluginView pluginOverlay = pluginOverlays.remove(overlayId); + + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + try { + scrollFrameLayout.removeView(pluginOverlay.getView()); + pluginOverlay.getView().removeView(touchableWrappers.remove(overlayId)); + + //Log.d("MyPluginLayout", "--> removePluginMap / overlayId = " + overlayId); + + + //mActivity.getWindow().getDecorView().requestFocus(); + } catch (Exception e) { + // ignore + //e.printStackTrace(); + } + } + }); + return pluginOverlay; + } + + public void addPluginOverlay(final IPluginView pluginOverlay) { + if (pluginOverlay.getDivId() == null) { + return; + } + + if (!HTMLNodes.containsKey(pluginOverlay.getDivId())) { + Log.e(TAG, "----> 200x200"); + Bundle dummyInfo = new Bundle(); + dummyInfo.putDouble("offsetX", 0); + dummyInfo.putDouble("offsetY", 3000); + dummyInfo.putBoolean("isDummy", true); + HTMLNodes.put(pluginOverlay.getDivId(), dummyInfo); + HTMLNodeRectFs.put(pluginOverlay.getDivId(), new RectF(0, 3000, 200, 200)); + } + pluginOverlays.put(pluginOverlay.getOverlayId(), pluginOverlay); + + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + + if (pluginOverlay.getView().getParent() == null) { + TouchableWrapper wrapper = new TouchableWrapper(context); + touchableWrappers.put(pluginOverlay.getOverlayId(), wrapper); + pluginOverlay.getView().addView(wrapper); + int depth = pluginOverlay.getViewDepth(); + int childCnt = scrollFrameLayout.getChildCount(); + View view; + int index = childCnt; + for (int i = childCnt - 1; i >= 0; i--) { + view = scrollFrameLayout.getChildAt(i); + if (view.getTag() == null) { + continue; + } + if (Integer.parseInt(view.getTag() + "") < depth) { + index = i; + break; + } + } + + scrollFrameLayout.addView(pluginOverlay.getView(), (childCnt - index)); + } + + //mActivity.getWindow().getDecorView().requestFocus(); + + //updateViewPosition(pluginMap.mapId); + + } + }); + } + + public void scrollTo(int x, int y) { + this.scrollView.scrollTo(x, y); + } + + public void inValidate() { + this.frontLayer.invalidate(); + } + + @Override + public void onScrollChanged() { +//Log.d("Layout", "---> onScrollChanged"); + scrollView.scrollTo(browserView.getScrollX(), browserView.getScrollY()); + } + + private class Overflow { + boolean cropX; + boolean cropY; + RectF rect; + } + + private class FrontLayerLayout extends FrameLayout { + + public FrontLayerLayout(Context context) { + super(context); + this.setWillNotDraw(false); + } + + private String findClickedDom(String domId, PointF clickPoint, boolean isMapChild, Overflow overflow) { + if (CACHE_FIND_DOM.containsKey(domId)) { + String cacheResult = CACHE_FIND_DOM.get(domId); + if ("(null)".equals(cacheResult)) { + cacheResult = null; + } + return cacheResult; + } + + Overflow overflow1 = null; + if (overflow != null) { + overflow1 = new Overflow(); + overflow1.cropX = overflow.cropX; + overflow1.cropY = overflow.cropY; + overflow1.rect = new RectF(overflow.rect.left, overflow.rect.top, overflow.rect.right, overflow.rect.bottom); + } + //Log.d(TAG, "-(findClickedDom)domId = " + domId + ", clickPoint = " + clickPoint.x + ", " + clickPoint.y); + + String maxDomId = null; + RectF rect, rect2, rect3; + Bundle domInfo = HTMLNodes.get(domId); + Bundle zIndexProp; + int containMapCnt = 0; + if (domInfo.containsKey("containMapIDs")) { + Set keys = domInfo.getBundle("containMapIDs").keySet(); + if (keys != null) { + containMapCnt = keys.size(); + } + } + isMapChild = isMapChild || domInfo.getBoolean("isMap", false); + + String pointerEvents = domInfo.getString("pointerEvents"); + String overflowX = domInfo.getString("overflowX"); + String overflowY = domInfo.getString("overflowY"); + if ("hidden".equals(overflowX) || "scroll".equals(overflowX) || + "hidden".equals(overflowY) || "scroll".equals(overflowY)) { + overflow1 = new Overflow(); + overflow1.cropX = "hidden".equals(overflowX) || "scroll".equals(overflowX); + overflow1.cropY = "hidden".equals(overflowY) || "scroll".equals(overflowY); + rect3 = HTMLNodeRectFs.get(domId); + overflow1.rect = new RectF(rect3.left, rect3.top, rect3.right, rect3.bottom); + } else if ("visible".equals(overflowX) || "visible".equals(overflowY)) { + if (overflow1 != null) { + overflow1.cropX = !"visible".equals(overflowX); + overflow1.cropY = !"visible".equals(overflowY); + } + } + + //if (overflow1 != null) { + // Log.d(TAG, "--domId = " + domId + ", domInfo = " + domInfo + "\n" + + // " overflow1 cropX= " + overflow1.cropX + ", cropY = " + overflow1.cropY + ",\n" + + // " rect = " + overflow1.rect.toShortString()); + //} else { + // Log.d(TAG, "--domId = " + domId + ", domInfo = " + domInfo); + //} + + zIndexProp = domInfo.getBundle("zIndex"); + ArrayList children = domInfo.getStringArrayList("children"); + if ((containMapCnt > 0 || isMapChild || "none".equals(pointerEvents) || zIndexProp.getBoolean("isInherit")) && children != null && children.size() > 0) { + int maxZindex = (int) Double.NEGATIVE_INFINITY; + int zIndexValue; + String childId, grandChildId; + ArrayList grandChildren; + for (int i = children.size() - 1; i >= 0; i--) { + childId = children.get(i); + domInfo = HTMLNodes.get(childId); + if (domInfo == null) { + continue; + } + + zIndexProp = domInfo.getBundle("zIndex"); + zIndexValue = zIndexProp.getInt("z"); + if (maxZindex < zIndexValue || zIndexProp.getBoolean("isInherit")) { + grandChildren = domInfo.getStringArrayList("children"); + if (grandChildren == null || grandChildren.size() == 0) { + rect = HTMLNodeRectFs.get(childId); + rect2 = new RectF(rect.left, rect.top, rect.right, rect.bottom); + if (overflow1 != null && !"root".equals(domId) ) { + overflow1.rect = new RectF(HTMLNodeRectFs.get(domId)); + if (overflow1.cropX) { + rect2.left = Math.max(rect2.left, overflow1.rect.left); + rect2.right = Math.min(rect2.right, overflow1.rect.right); + } + if (overflow1.cropY) { + rect2.top = Math.max(rect2.top, overflow1.rect.top); + rect2.bottom = Math.min(rect2.bottom, overflow1.rect.bottom); + } + } + if (!rect2.contains(clickPoint.x, clickPoint.y)) { + continue; + } + + //Log.d(TAG, "---childId = " + childId + ", domInfo = " + domInfo); + if ("none".equals(domInfo.getString("pointerEvents"))) { + continue; + } + //Log.d(TAG, "----childId = " + childId + ", maxZindex = " + maxZindex + ", zIndexValue = " + zIndexValue); + if (maxZindex < zIndexValue) { + maxZindex = zIndexValue; + maxDomId = childId; + //Log.i(TAG, "-----current = " + childId); + } + } else { + if (zIndexValue < maxZindex) { + //Log.i(TAG, "-----skip because " + childId + " is " + zIndexValue + " < " + maxZindex); + continue; + } + grandChildId = findClickedDom(childId, clickPoint, isMapChild, overflow1); + if (grandChildId == null) { + grandChildId = childId; + } else { + zIndexValue = HTMLNodes.get(grandChildId).getBundle("zIndex").getInt("z"); + } + rect = HTMLNodeRectFs.get(grandChildId); + //rect2 = new RectF(rect); + //Log.d(TAG, "---findClickedDom("+ childId + ") -> " + grandChildId + ",rect = " + rect2.toShortString()); + /* + if (overflow1 != null && !"root".equals(domId) ) { + overflow1.rect = new RectF(HTMLNodeRectFs.get(domId)); + if (overflow1.cropX) { + rect2.left = Math.max(rect2.left, overflow1.rect.left); + rect2.right = Math.min(rect2.right, overflow1.rect.right); + } + if (overflow1.cropY) { + rect2.top = Math.max(rect2.top, overflow1.rect.top); + rect2.bottom = Math.min(rect2.bottom, overflow1.rect.bottom); + } + //Log.d(TAG, "---overflow(" + domId + ") -> " + overflow1.rect.toShortString() + ", \n"+ + // " str=" + HTMLNodeRectFs.get(domId) + ",\n" + + // " overflowX = " + overflowX + ", overflowY = " + overflowY + + // " cropX = " + overflow1.cropX + ", cropY = " + overflow1.cropY); + } + */ + if (!rect.contains(clickPoint.x, clickPoint.y)) { + //Log.e(TAG, "----the click point is not in this element(" +grandChildId + "), rect = " + rect2.toShortString() + ", click = " + clickPoint.x + "," + clickPoint.y ); + continue; + } + + domInfo = HTMLNodes.get(grandChildId); + //Log.d(TAG, "----grandChildId = " + grandChildId + ", domInfo = " + domInfo); + if ("none".equals(domInfo.getString("pointerEvents"))) { + //Log.e(TAG, "-----grandChildId(" + grandChildId + ") is " + domInfo.getString("pointerEvents")); + continue; + } + //Log.d(TAG, "----grandChildId = " + grandChildId + ", maxZindex = " + maxZindex + ", zIndexValue = " + zIndexValue); + if (maxZindex < zIndexValue) { + maxZindex = zIndexValue; + maxDomId = grandChildId; + //Log.i(TAG, "-----current = " + grandChildId); + } + } + } + } + } + if (maxDomId == null && !"root".equals(domId)) { + if ("none".equals(pointerEvents)) { + CACHE_FIND_DOM.put(domId, "(null)"); + return null; + } + rect = HTMLNodeRectFs.get(domId); + rect2 = new RectF(rect.left, rect.top, rect.right, rect.bottom); +// if (overflow1 != null ) { +// overflow1.rect = new RectF(HTMLNodeRectFs.get(domId)); +// if (overflow1.cropX) { +// rect2.left = Math.max(rect2.left, overflow1.rect.left); +// rect2.right = Math.min(rect2.right, overflow1.rect.right); +// } +// if (overflow1.cropY) { +// rect2.top = Math.max(rect2.top, overflow1.rect.top); +// rect2.bottom = Math.min(rect2.bottom, overflow1.rect.bottom); +// } +// } + if (!rect2.contains(clickPoint.x, clickPoint.y)) { + //Log.e(TAG, "--the click point is not in this element(" +domId + "), rect = " + rect2.toShortString() + ", click = " + clickPoint.x + "," + clickPoint.y ); + CACHE_FIND_DOM.put(domId, "(null)"); + return null; + } + maxDomId = domId; + } + if (maxDomId == null) { + CACHE_FIND_DOM.put(domId, "(null)"); + } else { + CACHE_FIND_DOM.put(domId, maxDomId); + } + //Log.i(TAG, "-maxDomId = " + maxDomId + ", domId = " + domId); + + return maxDomId; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (pluginOverlays == null || pluginOverlays.size() == 0) { + return false; + } + MyPluginLayout.this.stopFlag = true; + + int action = event.getAction(); + //Log.d("FrontLayerLayout", "----> action = " + action + ", isScrolling = " + isScrolling); + + // The scroll action that started in the browser region is end. + isScrolling = action != MotionEvent.ACTION_UP && isScrolling; + if (isScrolling) { + MyPluginLayout.this.stopFlag = false; + return false; + } + + + IPluginView pluginOverlay; + Iterator> iterator = pluginOverlays.entrySet().iterator(); + Entry entry; + + PointF clickPoint = new PointF(event.getX(), event.getY()); + + RectF drawRect; + + synchronized (_lockHtmlNodes) { + CACHE_FIND_DOM.clear(); + + String clickedDomId = findClickedDom("root", clickPoint, false, null); + //Log.d(TAG, "----clickedDomId = " + clickedDomId); + while (iterator.hasNext()) { + entry = iterator.next(); + pluginOverlay = entry.getValue(); + + //----------------------- + // Is the map clickable? + //----------------------- + if (!pluginOverlay.getVisible() || !pluginOverlay.getClickable()) { + continue; + } + + if (pluginOverlay.getDivId() == null) { + continue; + } + + //------------------------------------------------ + // Is the clicked point is in the map rectangle? + //------------------------------------------------ + drawRect = HTMLNodeRectFs.get(pluginOverlay.getDivId()); + if (drawRect == null || !drawRect.contains(clickPoint.x, clickPoint.y)) { + continue; + } + + if (pluginOverlay.getDivId().equals(clickedDomId)) { + return true; + } + + } + } + isScrolling = (action == MotionEvent.ACTION_DOWN) || isScrolling; + + //browserView.requestFocus(View.FOCUS_DOWN); + return false; + } + + + @Override + protected void onDraw(Canvas canvas) { + if (isSuspended || HTMLNodes.isEmpty() || !isDebug) { + return; + } + + IPluginView pluginOverlay; + Iterator> iterator = pluginOverlays.entrySet().iterator(); + Entry entry; + RectF mapRect; + synchronized (_lockHtmlNodes) { + while (iterator.hasNext()) { + entry = iterator.next(); + pluginOverlay = entry.getValue(); + if (pluginOverlay.getDivId() == null) { + continue; + } + mapRect = HTMLNodeRectFs.get(pluginOverlay.getDivId()); + + debugPaint.setColor(Color.argb(100, 0, 255, 0)); + canvas.drawRect(mapRect, debugPaint); + } + } + + + } + } + + private class TouchableWrapper extends FrameLayout { + + public TouchableWrapper(Context context) { + super(context); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + int action = event.getAction(); + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP) { + scrollView.requestDisallowInterceptTouchEvent(true); + } + return super.dispatchTouchEvent(event); + } + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/ObjectCache.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/ObjectCache.java new file mode 100644 index 0000000..12df5d2 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/ObjectCache.java @@ -0,0 +1,46 @@ +package plugin.google.maps; + +import android.util.LruCache; + +import java.util.HashSet; + +public class ObjectCache { + public final HashSet keys = new HashSet(); + private LruCache objects = new LruCache(1024 * 1024 * 10); + + public boolean containsKey(String key) { + return keys.contains(key); + } + + public void put(String key, Object object) { + keys.add(key); + objects.put(key, object); + } + + public Object remove(String key) { + keys.remove(key); + return objects.remove(key); + } + + public Object get(String key) { + return objects.get(key); + } + + public int size() { + return objects.size(); + } + + + public void clear() { + for (String key : keys) { + objects.remove(key); + } + keys.clear(); + } + + public void destroy() { + objects.evictAll(); + } + + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginAsyncInterface.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginAsyncInterface.java new file mode 100644 index 0000000..322eeb6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginAsyncInterface.java @@ -0,0 +1,6 @@ +package plugin.google.maps; + +public interface PluginAsyncInterface { + public void onPostExecute(Object object); + public void onError(String errorMsg); +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginCircle.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginCircle.java new file mode 100644 index 0000000..8b53685 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginCircle.java @@ -0,0 +1,267 @@ +package plugin.google.maps; + +import com.google.android.gms.maps.model.Circle; +import com.google.android.gms.maps.model.CircleOptions; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; + +import org.apache.cordova.CallbackContext; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class PluginCircle extends MyPlugin implements MyPluginInterface { + + private String circleHashCode; + + /** + * Create circle + * @param args + * @param callbackContext + * @throws JSONException + */ + public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final CircleOptions circleOptions = new CircleOptions(); + int color; + final JSONObject properties = new JSONObject(); + + JSONObject opts = args.getJSONObject(1); + final String hashCode = args.getString(2); + circleHashCode = hashCode; + + if (opts.has("center")) { + JSONObject center = opts.getJSONObject("center"); + circleOptions.center(new LatLng(center.getDouble("lat"), center.getDouble("lng"))); + } + if (opts.has("radius")) { + circleOptions.radius(opts.getDouble("radius")); + } + if (opts.has("strokeColor")) { + color = PluginUtil.parsePluginColor(opts.getJSONArray("strokeColor")); + circleOptions.strokeColor(color); + } + if (opts.has("fillColor")) { + color = PluginUtil.parsePluginColor(opts.getJSONArray("fillColor")); + circleOptions.fillColor(color); + } + if (opts.has("strokeWidth")) { + circleOptions.strokeWidth((int)(opts.getDouble("strokeWidth") * density)); + } + if (opts.has("visible")) { + circleOptions.visible(opts.getBoolean("visible")); + } + if (opts.has("zIndex")) { + circleOptions.zIndex(opts.getInt("zIndex")); + } + if (opts.has("clickable")) { + properties.put("isClickable", opts.getBoolean("clickable")); + } else { + properties.put("isClickable", true); + } + properties.put("isVisible", circleOptions.isVisible()); + + // Since this plugin provide own click detection, + // disable default clickable feature. + circleOptions.clickable(false); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Circle circle = map.addCircle(circleOptions); + circle.setTag(hashCode); + pluginMap.objects.put("circle_" + hashCode, circle); + + pluginMap.objects.put("circle_property_" + hashCode, properties); + + // Recalculate the circle bounds + LatLngBounds bounds = PluginUtil.getBoundsFromCircle(circleOptions.getCenter(), circleOptions.getRadius()); + pluginMap.objects.put("circle_bounds_" + hashCode, bounds); + + JSONObject result = new JSONObject(); + try { + result.put("hashCode", hashCode); + result.put("__pgmId", "circle_" + hashCode); + callbackContext.success(result); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error(e.getMessage() + ""); + } + } + }); + + } + + + /** + * set center + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setCenter(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final LatLng center = new LatLng(args.getDouble(1), args.getDouble(2)); + final Circle circle = this.getCircle(id); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Recalculate the circle bounds + String propertyId = "circle_bounds_" + circleHashCode; + LatLngBounds bounds = PluginUtil.getBoundsFromCircle(circle.getCenter(), circle.getRadius()); + pluginMap.objects.put(propertyId, bounds); + + circle.setCenter(center); + callbackContext.success(); + } + }); + } + + /** + * set fill color + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setFillColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + int color = PluginUtil.parsePluginColor(args.getJSONArray(1)); + this.setInt("setFillColor", id, color, callbackContext); + } + + /** + * set stroke color + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setStrokeColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + int color = PluginUtil.parsePluginColor(args.getJSONArray(1)); + this.setInt("setStrokeColor", id, color, callbackContext); + } + + /** + * set stroke width + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setStrokeWidth(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + float width = (float)(args.getDouble(1) * density); + this.setFloat("setStrokeWidth", id, width, callbackContext); + } + + /** + * set radius + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setRadius(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final float radius = (float) args.getDouble(1); + final Circle circle = this.getCircle(id); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + // Recalculate the circle bounds + String propertyId = "circle_bounds_" + circleHashCode; + LatLngBounds bounds = PluginUtil.getBoundsFromCircle(circle.getCenter(), circle.getRadius()); + pluginMap.objects.put(propertyId, bounds); + + // Update the overlay + circle.setRadius(radius); + callbackContext.success(); + } + }); + + } + + /** + * set z-index + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + float zIndex = (float) args.getDouble(1); + this.setFloat("setZIndex", id, zIndex, callbackContext); + } + + + /** + * Set visibility for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final boolean isVisible = args.getBoolean(1); + + final Circle circle = this.getCircle(id); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + circle.setVisible(isVisible); + } + }); + String propertyId = "circle_property_" + circleHashCode; + JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId); + properties.put("isVisible", isVisible); + pluginMap.objects.put(propertyId, properties); + callbackContext.success(); + } + + /** + * Set clickable for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final boolean clickable = args.getBoolean(1); + String propertyId = id.replace("circle_", "circle_property_"); + JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId); + properties.put("isClickable", clickable); + pluginMap.objects.put(propertyId, properties); + callbackContext.success(); + } + /** + * Remove the circle + * @param args + * @param callbackContext + * @throws JSONException + */ + public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String id = args.getString(0); + final Circle circle = this.getCircle(id); + if (circle == null) { + callbackContext.success(); + return; + } + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + circle.remove(); + if (pluginMap.objects != null) { + pluginMap.objects.remove(id); + } + callbackContext.success(); + } + }); + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginEnvironment.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginEnvironment.java new file mode 100644 index 0000000..df8eb98 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginEnvironment.java @@ -0,0 +1,133 @@ +package plugin.google.maps; + +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.os.Handler; +import android.util.Log; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginManager; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; + +import java.lang.reflect.Method; + +public class PluginEnvironment extends CordovaPlugin { + public PluginManager pluginManager; + + @Override + public void initialize(final CordovaInterface cordova, final CordovaWebView webView) { + pluginManager = webView.getPluginManager(); + } + + @Override + public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) { + cordova.getThreadPool().submit(new Runnable() { + @Override + public void run() { + try { + Method method = PluginEnvironment.this.getClass().getDeclaredMethod(action, JSONArray.class, CallbackContext.class); + if (!method.isAccessible()) { + method.setAccessible(true); + } + method.invoke(PluginEnvironment.this, args, callbackContext); + } catch (Exception e) { + Log.e("CordovaLog", "An error occurred", e); + callbackContext.error(e.toString()); + } + } + }); + + return true; + } + + @SuppressWarnings("unused") + public void isAvailable(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + // ------------------------------ + // Check of Google Play Services + // ------------------------------ + int checkGooglePlayServices = + GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(cordova.getActivity()); + if (checkGooglePlayServices != ConnectionResult.SUCCESS) { + String errorMsg = GoogleApiAvailability.getInstance().getErrorString(checkGooglePlayServices); + callbackContext.error(errorMsg); + + try { + cordova.getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.gms"))); + } catch (android.content.ActivityNotFoundException anfe) { + cordova.getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=com.google.android.gms"))); + } + + // End the app (in order to prevent lots of crashes) + cordova.getActivity().finish(); + + return; + } + + // ------------------------------ + // Check of Google Maps Android API v2 + // ------------------------------ + try { + @SuppressWarnings({ "rawtypes" }) + Class GoogleMapsClass = Class.forName("com.google.android.gms.maps.GoogleMap"); + } catch (Exception e) { + Log.e("GoogleMaps", "Error", e); + callbackContext.error(e.getMessage()); + return; + } + callbackContext.success(); + } + + + + /** + * Set the app background + * @param args Parameters given from JavaScript side + * @param callbackContext Callback contect for sending back the result. + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setBackGroundColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + JSONArray rgba = args.getJSONArray(0); + int backgroundColor = Color.WHITE; + + if (rgba != null && rgba.length() == 4) { + backgroundColor = PluginUtil.parsePluginColor(rgba); + } + final int finalBackgroundColor = backgroundColor; + + + final CordovaGoogleMaps googleMaps = (CordovaGoogleMaps) pluginManager.getPlugin("CordovaGoogleMaps"); + + Handler handler = new Handler(cordova.getActivity().getMainLooper()); + handler.postDelayed(new Runnable() { + public void run() { + googleMaps.mPluginLayout.setBackgroundColor(finalBackgroundColor); + callbackContext.success(); + } + }, googleMaps.initialized ? 0 : 250); + } + + @SuppressWarnings("unused") + public Boolean getLicenseInfo(JSONArray args, final CallbackContext callbackContext) { + callbackContext.success("Google Maps Android API v2 does not need this method anymore. But for iOS, you still need to display the lincense."); + return true; + } + + @SuppressWarnings("unused") + public Boolean setEnv(JSONArray args, final CallbackContext callbackContext) { + // stub + callbackContext.success(); + return true; + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginGeocoder.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginGeocoder.java new file mode 100644 index 0000000..c2cda46 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginGeocoder.java @@ -0,0 +1,252 @@ +package plugin.google.maps; + +import android.location.Address; +import android.location.Geocoder; +import android.os.Bundle; + +import com.google.android.gms.maps.model.LatLngBounds; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class PluginGeocoder extends CordovaPlugin { + + // In order to prevent the TOO_MANY_REQUEST_ERROR (block by Google because too many request in short period), + // restricts the number of parallel threads. + // + // According from my tests, 5 threads are the best setting. + private static ExecutorService executorService = Executors.newFixedThreadPool(5); + private static Geocoder geocoder = null; + + + public void initialize(CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + if (geocoder == null) { + geocoder = new Geocoder(cordova.getActivity()); + } + } + + + @Override + public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) { + executorService.submit(new Runnable() { + @Override + public void run() { + try { + if ("geocode".equals(action)) { + PluginGeocoder.this.geocode(args, callbackContext); + } else { + callbackContext.error("Method: Geocoder." + action + "() is not found."); + } + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error(e.getMessage() + ""); + } + } + }); + return true; + } + + @SuppressWarnings("unused") + private void geocode(final JSONArray args, + final CallbackContext callbackContext) throws JSONException, IOException { + + JSONObject opts = args.getJSONObject(0); + List
geoResults = null; + JSONArray results = new JSONArray(); + Iterator
iterator = null; + int retryLimit = 10; + + // Geocoding + if (!opts.has("position") && opts.has("address")) { + String address = opts.getString("address"); + if (opts.has("bounds")) { + if (opts.has("bounds")) { + JSONArray points = opts.getJSONArray("bounds"); + LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points); + + boolean retry = true; + while (retry && retryLimit > 0) { + retry = false; + try { + geoResults = geocoder.getFromLocationName(address, 5, + bounds.southwest.latitude, bounds.southwest.longitude, + bounds.northeast.latitude, bounds.northeast.longitude); + } catch (IOException e) { + if ("Timed out waiting for response from server".equals(e.getMessage())) { + retry = true; + retryLimit--; + try { + Thread.sleep((int)(150 + Math.random() * 100)); // wait (150 + random)ms before retry + } catch (InterruptedException e1) { + //e1.printStackTrace(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + if (geoResults.size() == 0) { + JSONObject methodResult = new JSONObject(); + methodResult.put("idx", opts.getInt("idx")); + methodResult.put("results", new JSONArray()); + + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, methodResult); + callbackContext.sendPluginResult(pluginResult); + + return; + } + iterator = geoResults.iterator(); + } + } else { + + boolean retry = true; + while (retry) { + retry = false; + try { + geoResults = geocoder.getFromLocationName(address, 5); + } catch (IOException e) { + if ("Timed out waiting for response from server".equals(e.getMessage())) { + retry = true; + try { + Thread.sleep((int)(150 + Math.random() * 100)); // wait (150 + random)ms before retry + } catch (InterruptedException e1) { + //e1.printStackTrace(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + if (geoResults == null || geoResults.size() == 0) { + JSONObject methodResult = new JSONObject(); + methodResult.put("idx", opts.getInt("idx")); + methodResult.put("results", new JSONArray()); + + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, methodResult); + callbackContext.sendPluginResult(pluginResult); + + return; + } + iterator = geoResults.iterator(); + } + } + + // Reverse geocoding + if (opts.has("position") && !opts.has("address")) { + JSONObject position = opts.getJSONObject("position"); + + boolean retry = true; + while (retry && retryLimit > 0) { + retry = false; + try { + geoResults = geocoder.getFromLocation( + position.getDouble("lat"), + position.getDouble("lng"), 5); + } catch (IOException e) { + if ("Timed out waiting for response from server".equals(e.getMessage())) { + retry = true; + retryLimit--; + try { + Thread.sleep((int)(150 + Math.random() * 100)); // wait (150 + random)ms before retry + } catch (InterruptedException e1) { + //e1.printStackTrace(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (geoResults == null || geoResults.size() == 0) { + //------------- + // No results + //------------- + JSONObject methodResult = new JSONObject(); + methodResult.put("idx", opts.getInt("idx")); + methodResult.put("results", new JSONArray()); + + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, methodResult); + callbackContext.sendPluginResult(pluginResult); + + return; + } + iterator = geoResults.iterator(); + } + + if (iterator == null) { + callbackContext.error( "Invalid request for geocoder"); + return; + } + + while(iterator.hasNext()) { + JSONObject result = new JSONObject(); + Address addr = iterator.next(); + + JSONObject position = new JSONObject(); + position.put("lat", addr.getLatitude()); + position.put("lng", addr.getLongitude()); + result.put("position", position); + + result.put("locality", addr.getLocality()); + result.put("adminArea", addr.getAdminArea()); + result.put("country", addr.getCountryName()); + result.put("countryCode", addr.getCountryCode()); + result.put("locale", addr.getLocale()); + result.put("postalCode", addr.getPostalCode()); + result.put("subAdminArea", addr.getSubAdminArea()); + result.put("subLocality", addr.getSubLocality()); + result.put("subThoroughfare", addr.getSubThoroughfare()); + result.put("thoroughfare", addr.getThoroughfare()); + + + JSONObject extra = new JSONObject(); + extra.put("featureName", addr.getFeatureName()); + extra.put("phone", addr.getPhone()); + extra.put("permises", addr.getPremises()); + extra.put("url", addr.getUrl()); + + JSONArray lines = new JSONArray(); + // Handled cases where we have only one line address. + for (int i = 0; i <= addr.getMaxAddressLineIndex(); i++) { + lines.put(addr.getAddressLine(i)); + } + extra.put("lines", lines); + + + Bundle extraInfo = addr.getExtras(); + if (extraInfo != null) { + Set keys = extraInfo.keySet(); + Iterator keyIterator = keys.iterator(); + String key; + while(keyIterator.hasNext()) { + key = keyIterator.next(); + extra.put(key, extraInfo.get(key)); + } + } + result.put("extra", extra); + results.put(result); + } + + JSONObject methodResult = new JSONObject(); + methodResult.put("idx", opts.getInt("idx")); + methodResult.put("results", results); + + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, methodResult); + callbackContext.sendPluginResult(pluginResult); + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginGroundOverlay.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginGroundOverlay.java new file mode 100644 index 0000000..e5ba35e --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginGroundOverlay.java @@ -0,0 +1,661 @@ +package plugin.google.maps; + +import android.graphics.Bitmap; +import android.os.AsyncTask; + +import com.google.android.gms.maps.model.BitmapDescriptor; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.GroundOverlay; +import com.google.android.gms.maps.model.GroundOverlayOptions; +import com.google.android.gms.maps.model.LatLngBounds; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaWebView; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Set; + +public class PluginGroundOverlay extends MyPlugin implements MyPluginInterface { + + private HashMap imageLoadingTasks = new HashMap(); + private final Object semaphore = new Object(); + private HashMap overlayImage = new HashMap(); + private boolean _clearDone = false; + + @Override + public void initialize(final CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + } + + /** + * Create ground overlay + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void create(JSONArray args, CallbackContext callbackContext) throws JSONException { + JSONObject opts = args.getJSONObject(1); + String hashCode = args.getString(2); + _createGroundOverlay(hashCode, opts, callbackContext); + } + + public void _createGroundOverlay(final String idBase, final JSONObject opts, final CallbackContext callbackContext) throws JSONException { + final GroundOverlayOptions options = new GroundOverlayOptions(); + final JSONObject properties = new JSONObject(); + options.anchor(0.5f, 0.5f); + + if (opts.has("anchor")) { + JSONArray anchor = opts.getJSONArray("anchor"); + options.anchor((float)anchor.getDouble(0), (float)anchor.getDouble(1)); + } + if (opts.has("bearing")) { + options.bearing((float)opts.getDouble("bearing")); + } + if (opts.has("opacity")) { + options.transparency(1 - (float)opts.getDouble("opacity")); + } + if (opts.has("zIndex")) { + options.zIndex((float)opts.getDouble("zIndex")); + } + if (opts.has("visible")) { + options.visible(opts.getBoolean("visible")); + } + if (opts.has("bounds")) { + JSONArray points = opts.getJSONArray("bounds"); + LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points); + options.positionFromBounds(bounds); + } + if (opts.has("clickable")) { + properties.put("isClickable", opts.getBoolean("clickable")); + } else { + properties.put("isClickable", true); + } + properties.put("isVisible", options.isVisible()); + + // Since this plugin provide own click detection, + // disable default clickable feature. + options.clickable(false); + + // Load image + final String imageUrl = opts.getString("url"); + + setImage_(imageUrl, new PluginAsyncInterface() { + + @Override + public void onPostExecute(Object object) { + if (object == null) { + callbackContext.error("Cannot create a ground overlay"); + return; + } + + AsyncLoadImage.AsyncLoadImageResult result = (AsyncLoadImage.AsyncLoadImageResult)object; + BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(result.image); + options.image(bitmapDescriptor); + //options.zIndex(Calendar.getInstance().getTimeInMillis()); + GroundOverlay groundOverlay = self.map.addGroundOverlay(options); + overlayImage.put("groundoverlay_" + idBase, result.image); + + groundOverlay.setTag(idBase); + + pluginMap.objects.put("groundoverlay_" + idBase, groundOverlay); + + pluginMap.objects.put("groundoverlay_bounds_" + idBase, groundOverlay.getBounds()); + + pluginMap.objects.put("groundoverlay_property_" + idBase, properties); + + pluginMap.objects.put("groundoverlay_initOpts_" + idBase, opts); + + + JSONObject resultJSON = new JSONObject(); + try { + resultJSON.put("hashCode", idBase); + resultJSON.put("__pgmId", "groundoverlay_" + idBase); + } catch (Exception e) { + e.printStackTrace(); + } + callbackContext.success(resultJSON); + } + + @Override + public void onError(String errorMsg) { + callbackContext.error(errorMsg); + } + + }); + } + + + @Override + protected void clear() { + _clearDone = false; + synchronized (semaphore) { + + //-------------------------------------- + // clean up properties as much as possible + //-------------------------------------- + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Set keySet = pluginMap.objects.keys; + if (keySet.size() > 0) { + String[] objectIdArray = keySet.toArray(new String[keySet.size()]); + + synchronized (pluginMap.objects) { + Bitmap image; + for (String objectId : objectIdArray) { + if (pluginMap.objects.containsKey(objectId)) { + if (objectId.startsWith("groundoverlay_") && + !objectId.startsWith("groundoverlay_property_") && + !objectId.startsWith("groundoverlay_initOpts_") && + !objectId.startsWith("groundoverlay_bounds_")) { + GroundOverlay groundOverlay = (GroundOverlay) pluginMap.objects.remove(objectId); + image = overlayImage.remove(objectId); + if (image != null && !image.isRecycled()) { + image.recycle(); + } + groundOverlay.setTag(null); + groundOverlay.remove(); + groundOverlay = null; + } else { + Object object = pluginMap.objects.remove(objectId); + object = null; + } + } + } + } + + synchronized (semaphore) { + _clearDone = true; + semaphore.notify(); + } + } + + } + }); + + Set keySet = pluginMap.objects.keys; + if (keySet.size() > 0) { + try { + if (!_clearDone) { + semaphore.wait(1000); + } + } catch (InterruptedException e) { + // ignore + //e.printStackTrace(); + } + } + } + } + + /** + * Remove this tile layer + * @param args + * @param callbackContext + * @throws JSONException + */ + public void remove(JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String id = args.getString(0); + final GroundOverlay groundOverlay = (GroundOverlay)pluginMap.objects.get(id); + if (groundOverlay == null) { + callbackContext.success(); + return; + } + + synchronized (pluginMap.objects) { + pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_property_")); + pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_initOpts_")); + pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_bounds_")); + } + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + synchronized (pluginMap.objects) { + GroundOverlay groundOverlay = (GroundOverlay) pluginMap.objects.remove(id); + if (groundOverlay != null) { + Bitmap image = overlayImage.remove(id); + if (image != null && !image.isRecycled()) { + image.recycle(); + } + groundOverlay.remove(); + } + } + callbackContext.success(); + } + }); + } + + + /** + * Set image of the ground-overlay + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setImage(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String id = args.getString(0); + final String url = args.getString(1); + final String idBase = id.replace("groundoverlay_", ""); + + String propertyId = "groundoverlay_initOpts_" + idBase; + final JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId); + opts.put("url", url); + pluginMap.objects.put(propertyId, opts); + + setImage_(url, new PluginAsyncInterface() { + @Override + public void onPostExecute(Object object) { + if (object == null) { + callbackContext.error("[error]groundoverlay.setImage(" + url + ")"); + return; + } + AsyncLoadImage.AsyncLoadImageResult result = (AsyncLoadImage.AsyncLoadImageResult) object; + GroundOverlay groundOverlay = getGroundOverlay(id); + if (groundOverlay != null) { + Bitmap currentBmp = overlayImage.remove(id); + if (currentBmp != null) { + currentBmp.recycle(); + } + } + if (result.image != null) { + overlayImage.put(id, result.image); + groundOverlay.setImage(BitmapDescriptorFactory.fromBitmap(result.image)); + callbackContext.success(); + } else { + callbackContext.error("[error]groundoverlay.setImage(" + url + ")"); + } + } + + @Override + public void onError(String errorMsg) { + + } + }); + } + + + /** + * Set bounds + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setBounds(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final GroundOverlay groundOverlay = (GroundOverlay)pluginMap.objects.get(id); + + String propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_"); + JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId); + + JSONArray points = args.getJSONArray(1); + opts.put("bounds", points); + pluginMap.objects.put(propertyId, opts); + + final LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points); + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + groundOverlay.setPositionFromBounds(bounds); + } + }); + + String boundsId = id.replace("groundoverlay_", "groundoverlay_bounds_"); + pluginMap.objects.put(boundsId, bounds); + + callbackContext.success(); + } + + /** + * Set opacity + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setOpacity(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + float opacity = (float)args.getDouble(1); + String id = args.getString(0); + + String propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_"); + JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId); + opts.put("opacity", opacity); + pluginMap.objects.put(propertyId, opts); + + this.setFloat("setTransparency", id, 1 - opacity, callbackContext); + } + /** + * Set bearing + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setBearing(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + float bearing = (float)args.getDouble(1); + String id = args.getString(0); + + String propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_"); + JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId); + opts.put("bearing", bearing); + pluginMap.objects.put(propertyId, opts); + + this.setFloat("setBearing", id, bearing, callbackContext); + } + /** + * set z-index + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + float zIndex = (float) args.getDouble(1); + + String propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_"); + JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId); + opts.put("zIndex", zIndex); + pluginMap.objects.put(propertyId, opts); + + this.setFloat("setZIndex", id, zIndex, callbackContext); + } + + + /** + * Set visibility for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final boolean isVisible = args.getBoolean(1); + + final GroundOverlay groundOverlay = this.getGroundOverlay(id); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + groundOverlay.setVisible(isVisible); + } + }); + String propertyId = id.replace("groundoverlay_", "groundoverlay_property_"); + JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId); + properties.put("isVisible", isVisible); + pluginMap.objects.put(propertyId, properties); + + propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_"); + JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId); + opts.put("visible", isVisible); + pluginMap.objects.put(propertyId, opts); + + callbackContext.success(); + } + + /** + * Set clickable for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final boolean clickable = args.getBoolean(1); + String propertyId = id.replace("groundoverlay_", "groundoverlay_property_"); + JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId); + properties.put("isClickable", clickable); + pluginMap.objects.put(propertyId, properties); + callbackContext.success(); + } + + + private void setImage_(final String imgUrl, final PluginAsyncInterface callback) { + if (imgUrl == null) { + callback.onPostExecute(null); + return; + } + + final AsyncLoadImage.AsyncLoadImageOptions imageOptions = new AsyncLoadImage.AsyncLoadImageOptions(); + imageOptions.height = -1; + imageOptions.width = -1; + imageOptions.noCaching = true; + imageOptions.url = imgUrl; + final int taskId = imageOptions.hashCode(); + + final AsyncLoadImageInterface onComplete = new AsyncLoadImageInterface() { + + @Override + public void onPostExecute(AsyncLoadImage.AsyncLoadImageResult result) { + if (result == null || result.image == null) { + callback.onError("Can not read image from " + imgUrl); + imageLoadingTasks.remove(taskId).cancel(true); + return; + } + + callback.onPostExecute(result); + + imageLoadingTasks.remove(taskId).cancel(true); + } + }; + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + AsyncLoadImage task = new AsyncLoadImage(cordova, webView, imageOptions, onComplete); + //cordova.getActivity().runOnUiThread(new Runnable() { + // @Override + // public void run() { + // task.execute(); + // } + //}); + task.execute(); + imageLoadingTasks.put(taskId, task); + } + }); + + +/* + if (imgUrl == null) { + callback.onPostExecute(null); + return; + } + + if (!imgUrl.contains("://") && + !imgUrl.startsWith("/") && + !imgUrl.startsWith("www/") && + !imgUrl.startsWith("data:image") && + !imgUrl.startsWith("./") && + !imgUrl.startsWith("../")) { + imgUrl = "./" + imgUrl; + } + if (imgUrl.startsWith("./") || imgUrl.startsWith("../")) { + imgUrl = imgUrl.replace("././", "./"); + String currentPage = CURRENT_PAGE_URL; + currentPage = currentPage.replaceAll("[^\\/]*$", ""); + imgUrl = currentPage + "/" + imgUrl; + } + + if (imgUrl == null) { + callback.onPostExecute(null); + return; + } + + final String imageUrl = imgUrl; + if (imageUrl.indexOf("http") != 0) { + //---------------------------------- + // Load img from local file + //---------------------------------- + AsyncTask task = new AsyncTask() { + + @Override + protected Bitmap doInBackground(Void... params) { + String imgUrl = imageUrl; + if (imgUrl == null) { + return null; + } + + Bitmap image = null; + if (imgUrl.indexOf("cdvfile://") == 0) { + CordovaResourceApi resourceApi = webView.getResourceApi(); + imgUrl = PluginUtil.getAbsolutePathFromCDVFilePath(resourceApi, imgUrl); + } + if (imgUrl == null) { + return null; + } + + if (imgUrl.indexOf("data:image/") == 0 && imgUrl.contains(";base64,")) { + String[] tmp = imgUrl.split(","); + image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]); + } else if (imgUrl.indexOf("file://") == 0 && + !imgUrl.contains("file:///android_asset/")) { + imgUrl = imgUrl.replace("file://", ""); + File tmp = new File(imgUrl); + if (tmp.exists()) { + image = BitmapFactory.decodeFile(imgUrl); + } else { + Log.w(TAG, "image is not found (" + imgUrl + ")"); + } + } else { + //Log.d(TAG, "imgUrl = " + imgUrl); + if (imgUrl.indexOf("file:///android_asset/") == 0) { + imgUrl = imgUrl.replace("file:///android_asset/", ""); + } + //Log.d(TAG, "imgUrl = " + imgUrl); + if (imgUrl.contains("./")) { + try { + boolean isAbsolutePath = imgUrl.startsWith("/"); + File relativePath = new File(imgUrl); + imgUrl = relativePath.getCanonicalPath(); + //Log.d(TAG, "imgUrl = " + imgUrl); + if (!isAbsolutePath) { + imgUrl = imgUrl.substring(1); + } + //Log.d(TAG, "imgUrl = " + imgUrl); + } catch (Exception e) { + e.printStackTrace(); + } + } + AssetManager assetManager = PluginGroundOverlay.this.cordova.getActivity().getAssets(); + InputStream inputStream; + try { + inputStream = assetManager.open(imgUrl); + image = BitmapFactory.decodeStream(inputStream); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + if (image == null) { + return null; + } + + return image; + } + + @Override + protected void onPostExecute(Bitmap image) { + if (image == null) { + callback.onPostExecute(null); + return; + } + + GroundOverlay groundOverlay = null; + try { + BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(image); + options.image(bitmapDescriptor); + groundOverlay = self.map.addGroundOverlay(options); + + callback.onPostExecute(groundOverlay); + + } catch (Exception e) { + Log.e(TAG,"PluginGroundOverlay: Warning - ground overlay method is called when ground overlay method has been disposed, wait for addGroundOverlay callback before calling more methods on the groundOverlay."); + //e.printStackTrace(); + try { + if (groundOverlay != null) { + groundOverlay.remove(); + } + } catch (Exception ignore) { + ignore = null; + } + callback.onError(e.getMessage() + ""); + } + } + }; + task.execute(); + imageLoadingTasks.add(task); + + + return; + } + + if (imageUrl.indexOf("http") == 0) { + //---------------------------------- + // Load img from on the internet + //---------------------------------- + int width = -1; + int height = -1; + AsyncLoadImage task = new AsyncLoadImage(userAgent, width, height, true, new AsyncLoadImageInterface() { + + @Override + public void onPostExecute(Bitmap image) { + + if (image == null) { + callback.onPostExecute(null); + return; + } + GroundOverlay groundOverlay = null; + try { + BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(image); + options.image(bitmapDescriptor); + groundOverlay = self.map.addGroundOverlay(options); + + image.recycle(); + callback.onPostExecute(groundOverlay); + } catch (Exception e) { + //e.printStackTrace(); + try { + if (groundOverlay != null) { + groundOverlay.remove(); + } + } catch (Exception ignore) { + ignore = null; + } + callback.onError(e.getMessage() + ""); + } + + } + + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imageUrl); + imageLoadingTasks.add(task); + } +*/ + } + + + @Override + public void onDestroy() { + super.onDestroy(); + + //-------------------------------------- + // Cancel tasks + //-------------------------------------- + cordova.getThreadPool().submit(new Runnable() { + @Override + public void run() { + AsyncTask task; + int i, ilen = imageLoadingTasks.size(); + for (i = 0; i < ilen; i++) { + task = imageLoadingTasks.remove(i); + task.cancel(true); + task = null; + } + imageLoadingTasks = null; + } + }); + + this.clear(); + + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginKmlOverlay.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginKmlOverlay.java new file mode 100644 index 0000000..b12d67f --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginKmlOverlay.java @@ -0,0 +1,434 @@ +package plugin.google.maps; + +import android.os.Bundle; + +import org.apache.cordova.CallbackContext; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; + +import za.co.twyst.tbxml.TBXML; + + +public class PluginKmlOverlay extends MyPlugin implements MyPluginInterface { + private HashMap styles = new HashMap(); + + private enum KML_TAG { + NOT_SUPPORTED, + + kml, + style, + styleurl, + stylemap, + schema, + coordinates + } + + /** + * Create kml overlay + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + final JSONObject opts = args.getJSONObject(1); + self = this; + if (!opts.has("url")) { + callbackContext.error("No kml file is specified"); + return; + } + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + String urlStr = null; + + try { + urlStr = opts.getString("url"); + } catch (JSONException e) { + e.printStackTrace(); + } + if (urlStr == null || urlStr.length() == 0) { + callbackContext.error("No kml file is specified"); + return; + } + + String currentPageUrl = webView.getUrl(); + if (!urlStr.contains("://") && + !urlStr.startsWith("/") && + !urlStr.startsWith("www/") && + !urlStr.startsWith("data:image") && + !urlStr.startsWith("./") && + !urlStr.startsWith("../")) { + urlStr = "./" + urlStr; + } + + if (currentPageUrl.startsWith("http://localhost") || + currentPageUrl.startsWith("http://127.0.0.1")) { + if (urlStr.contains("://")) { + urlStr = urlStr.replaceAll("http://.+?/", "file:///android_asset/www/"); + } else { + // Avoid WebViewLocalServer (because can not make a connection for some reason) + urlStr = "file:///android_asset/www/".concat(urlStr); + } + } + + + if (urlStr.startsWith("./") || urlStr.startsWith("../")) { + urlStr = urlStr.replace("././", "./"); + currentPageUrl = currentPageUrl.replaceAll("[^\\/]*$", ""); + urlStr = currentPageUrl + "/" + urlStr; + } + if (urlStr.startsWith("cdvfile://")) { + urlStr = PluginUtil.getAbsolutePathFromCDVFilePath(webView.getResourceApi(), urlStr); + } + + // Avoid WebViewLocalServer (because can not make a connection for some reason) + if (urlStr.contains("http://localhost") || urlStr.contains("http://127.0.0.1")) { + urlStr = urlStr.replaceAll("^http://[^\\/]+\\//", "file:///android_asset/www/"); + } + + + final String finalUrl = urlStr; + executorService.submit(new Runnable() { + @Override + public void run() { + Bundle result = loadKml(finalUrl); + callbackContext.success(PluginUtil.Bundle2Json(result)); + } + }); + } + }); + } + + private Bundle loadKml(String urlStr) { + + InputStream inputStream = getKmlContents(urlStr); + if (inputStream == null) { + return null; + } + try { + // KMZ is not ready yet.... sorry + +// if (urlStr.contains(".kmz")) { +// String cacheDirPath = cordova.getActivity().getCacheDir() + "/" + Integer.toString(urlStr.hashCode(), 16); +// File cacheDir = new File(cacheDirPath); +// if (!cacheDir.exists()) { +// cacheDir.mkdirs(); +// } +// ArrayList files = (PluginUtil.unpackZipFromBytes(inputStream, cacheDirPath)); +// inputStream.close(); +// for (File file : files) { +// if (file.getName().contains(".kml")) { +// inputStream = new FileInputStream(file); +// break; +// } +// } +// } + + + String line; + StringBuilder stringBuilder = new StringBuilder(); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader); + while ((line = bufferedReader.readLine()) != null) { + stringBuilder.append(line); + stringBuilder.append("\n"); + } + bufferedReader.close(); + + + TBXML tbxml = new TBXML(); + tbxml.parse(stringBuilder.toString()); + + KmlParserClass parser = new KmlParserClass(); + Bundle root = parser.parseXml(tbxml, tbxml.rootXMLElement()); + Bundle result = new Bundle(); + result.putBundle("schemas", parser.schemaHolder); + result.putBundle("styles", parser.styleHolder); + result.putBundle("root", root); + + + tbxml.release(); + inputStreamReader.close(); + inputStream.close(); + inputStream = null; + return result; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + class KmlParserClass { + public Bundle styleHolder = new Bundle(); + public Bundle schemaHolder = new Bundle(); + + + private Bundle parseXml(TBXML tbxml, long rootElement) { + Bundle result = new Bundle(); + String styleId, schemaId, txt, attrName; + int i; + String tagName = tbxml.elementName(rootElement).toLowerCase(); + long childNode; + Bundle styles, schema, extendedData; + ArrayList children; + ArrayList styleIDs; + + //Log.d(TAG, "--->tagName = " + tagName + "(" + rootElement + ")"); + result.putString("tagName", tagName); + + KML_TAG kmlTag = null; + try { + kmlTag = KML_TAG.valueOf(tagName); + } catch(Exception e) { + kmlTag = KML_TAG.NOT_SUPPORTED; + } + + long[] attributes = tbxml.listAttributesOfElement(rootElement); + for (i = 0; i < attributes.length; i++) { + attrName = tbxml.attributeName(attributes[i]); + result.putString(attrName, tbxml.attributeValue(attributes[i])); + } + + + switch (kmlTag) { + + case styleurl: + styleId = tbxml.textForElement(rootElement); + result.putString("styleId", styleId); + break; + + case stylemap: + case style: + + // Generate a style id for the tag + styleId = tbxml.valueOfAttributeNamed("id", rootElement); + if (styleId == null || styleId.isEmpty()) { + styleId = "__" + rootElement + "__"; + } + result.putString("styleId", styleId); + + // Store style information into the styleHolder. + styles = new Bundle(); + children = new ArrayList(); + childNode = tbxml.firstChild(rootElement); + while (childNode > 0) { + Bundle node = this.parseXml(tbxml, childNode); + if (node != null) { + if (node.containsKey("value")) { + styles.putString(node.getString("tagName"), node.getString("value")); + } else { + children.add(node); + } + } + childNode = tbxml.nextSibling(childNode); + } + if (children.size() > 0) { + styles.putParcelableArrayList("children", children); + } + styleHolder.putBundle(styleId, styles); + + + break; + + case schema: + + // Generate a schema id for the tag + schemaId = tbxml.valueOfAttributeNamed("id", rootElement); + if (schemaId == null || schemaId.isEmpty()) { + schemaId = "__" + rootElement + "__"; + } + + // Store schema information into the schemaHolder. + schema = new Bundle(); + schema.putString("name", tbxml.valueOfAttributeNamed("name", rootElement)); + children = new ArrayList(); + childNode = tbxml.firstChild(rootElement); + while (childNode > 0) { + Bundle node = this.parseXml(tbxml, childNode); + if (node != null) { + children.add(node); + } + childNode = tbxml.nextSibling(childNode); + } + if (children.size() > 0) { + schema.putParcelableArrayList("children", children); + } + schemaHolder.putBundle(schemaId, schema); + + + break; + case coordinates: + + + ArrayList latLngList = new ArrayList(); + + txt = tbxml.textForElement(rootElement); + txt = txt.replaceAll("\\s+", "\n"); + txt = txt.replaceAll("\\n+", "\n"); + String lines[] = txt.split("\n"); + String tmpArry[]; + Bundle latLng; + for (i = 0; i < lines.length; i++) { + lines[i] = lines[i].replaceAll("[^0-9,.\\-Ee]", ""); + if (!"".equals(lines[i])) { + tmpArry = lines[i].split(","); + latLng = new Bundle(); + latLng.putDouble("lat", Double.parseDouble(tmpArry[1])); + latLng.putDouble("lng", Double.parseDouble(tmpArry[0])); + latLngList.add(latLng); + } + } + + result.putParcelableArrayList(tagName, latLngList); + break; + + + + default: + + childNode = tbxml.firstChild(rootElement); + if (childNode != 0) { + children = new ArrayList(); + while (childNode != 0) { + Bundle node = this.parseXml(tbxml, childNode); + if (node != null) { + if (node.containsKey("styleId")) { + //-------------------------------------------- + // writtin in JavaScript + // result.styleIDs = result.styleIDs || []; + // result.styleIDs.push(node.styleId); + //-------------------------------------------- + styleIDs = result.getStringArrayList("styleIDs"); + if (styleIDs == null) { + styleIDs = new ArrayList(); + } + styleIDs.add(node.getString("styleId")); + result.putStringArrayList("styleIDs", styleIDs); + } else if (!("schema".equals(node.getString("tagName")))) { + children.add(node); + } + } + childNode = tbxml.nextSibling(childNode); + } + + result.putParcelableArrayList("children", children); + } else { + result.putString("value", tbxml.textForElement(rootElement)); + } + break; + } + + return result; + } + } + + + private InputStream getKmlContents(String urlStr) { + + InputStream inputStream; + try { + //Log.d("PluginKmlOverlay", "---> url = " + urlStr); + if (urlStr.startsWith("http://") || urlStr.startsWith("https://")) { + URL url = new URL(urlStr); + boolean redirect = true; + HttpURLConnection http = null; + String cookies = null; + int redirectCnt = 0; + while(redirect && redirectCnt < 10) { + redirect = false; + http = (HttpURLConnection)url.openConnection(); + http.setRequestMethod("GET"); + if (cookies != null) { + http.setRequestProperty("Cookie", cookies); + } + http.addRequestProperty("Accept-Language", "en-US,en;q=0.8"); + http.addRequestProperty("User-Agent", "Mozilla"); + http.setInstanceFollowRedirects(true); + HttpURLConnection.setFollowRedirects(true); + + // normally, 3xx is redirect + int status = http.getResponseCode(); + if (status != HttpURLConnection.HTTP_OK) { + if (status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_SEE_OTHER) + redirect = true; + } + if (redirect) { + // get redirect url from "location" header field + url = new URL(http.getHeaderField("Location")); + + // get the cookie if need, for login + cookies = http.getHeaderField("Set-Cookie"); + + // Disconnect the current connection + http.disconnect(); + redirectCnt++; + } + } + + inputStream = http.getInputStream(); + } else if (urlStr.indexOf("file://") == 0 && !urlStr.contains("file:///android_asset/") || + urlStr.indexOf("/") == 0) { + urlStr = urlStr.replace("file://", ""); + try { + boolean isAbsolutePath = urlStr.startsWith("/"); + File relativePath = new File(urlStr); + urlStr = relativePath.getCanonicalPath(); + //Log.d(TAG, "imgUrl = " + imgUrl); + if (!isAbsolutePath) { + urlStr = urlStr.substring(1); + } + //Log.d(TAG, "imgUrl = " + imgUrl); + } catch (Exception e) { + e.printStackTrace(); + } + //Log.d("PluginKmlOverlay", "---> url = " + urlStr); + inputStream = new FileInputStream(urlStr); + } else { + if (urlStr.indexOf("file:///android_asset/") == 0) { + urlStr = urlStr.replace("file:///android_asset/", ""); + } + + + try { + boolean isAbsolutePath = urlStr.startsWith("/"); + File relativePath = new File(urlStr); + urlStr = relativePath.getCanonicalPath(); + //Log.d(TAG, "imgUrl = " + imgUrl); + if (!isAbsolutePath) { + urlStr = urlStr.substring(1); + } + //Log.d(TAG, "imgUrl = " + imgUrl); + } catch (Exception e) { + e.printStackTrace(); + } + //Log.d("PluginKmlOverlay", "---> url = " + urlStr); + inputStream = cordova.getActivity().getResources().getAssets().open(urlStr); + } + + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + return inputStream; + + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginLocationService.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginLocationService.java new file mode 100644 index 0000000..b385cc2 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginLocationService.java @@ -0,0 +1,665 @@ +package plugin.google.maps; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.location.Location; +import android.location.LocationManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.v4.content.PermissionChecker; +import android.util.Log; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationResult; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +public class PluginLocationService extends CordovaPlugin { + private Activity activity; + private final String TAG = "PluginLocationService"; + private HashMap bufferForLocationDialog = new HashMap(); + + private final int ACTIVITY_LOCATION_DIALOG = 0x7f999900; // Invite the location dialog using Google Play Services + private final int ACTIVITY_LOCATION_PAGE = 0x7f999901; // Open the location settings page + + private GoogleApiClient googleApiClient = null; + + public void initialize(final CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + activity = cordova.getActivity(); + } + private static Location lastLocation = null; + private ArrayList regularAccuracyRequestList = new ArrayList(); + private ArrayList highAccuracyRequestList = new ArrayList(); + public static final Object semaphore = new Object(); + + public static void setLastLocation(Location location) { + // Sets the last location if the end user click on the mylocation (blue dot) + lastLocation = location; + } + + + @Override + public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + cordova.getThreadPool().submit(new Runnable() { + @Override + public void run() { + try { + if ("getMyLocation".equals(action)) { + PluginLocationService.this.getMyLocation(args, callbackContext); + } else if ("hasPermission".equals(action)) { + PluginLocationService.this.hasPermission(args, callbackContext); + } + + } catch (JSONException e) { + e.printStackTrace(); + } + } + }); + return true; + + } + + + + @SuppressWarnings("unused") + public void hasPermission(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + synchronized (semaphore) { + // Check geolocation permission. + boolean locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity().getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED; + callbackContext.success(locationPermission ? 1 : 0); + } + } + + + @SuppressWarnings("unused") + public void getMyLocation(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + synchronized (semaphore) { + + + + LocationManager locationManager = (LocationManager) this.activity.getSystemService(Context.LOCATION_SERVICE); + List providers = locationManager.getAllProviders(); + int availableProviders = 0; + //if (mPluginLayout != null && mPluginLayout.isDebug) { + Log.d(TAG, "---debug at getMyLocation(available providers)--"); + //} + Iterator iterator = providers.iterator(); + String provider; + boolean isAvailable; + while(iterator.hasNext()) { + provider = iterator.next(); + if ("passive".equals(provider)) { + continue; + } + isAvailable = locationManager.isProviderEnabled(provider); + if (isAvailable) { + availableProviders++; + } + //if (mPluginLayout != null && mPluginLayout.isDebug) { + Log.d(TAG, " " + provider + " = " + (isAvailable ? "" : "not ") + "available"); + //} + } + if (availableProviders == 0) { + JSONObject result = new JSONObject(); + try { + result.put("status", false); + result.put("error_code", "not_available"); + result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_no_location_providers")); + } catch (JSONException e) { + e.printStackTrace(); + } + callbackContext.error(result); + return; + } + + + + + + JSONObject params = args.getJSONObject(0); + boolean requestHighAccuracy = false; + if (params.has("enableHighAccuracy")) { + requestHighAccuracy = params.getBoolean("enableHighAccuracy"); + } + if (requestHighAccuracy && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { + JSONObject result = new JSONObject(); + try { + result.put("status", false); + result.put("error_code", "not_available"); + result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_no_location_service_is_disabled")); + } catch (JSONException e) { + e.printStackTrace(); + } + callbackContext.error(result); + return; + } + + // enableHighAccuracy = true -> PRIORITY_HIGH_ACCURACY + // enableHighAccuracy = false -> PRIORITY_BALANCED_POWER_ACCURACY + if (requestHighAccuracy) { + highAccuracyRequestList.add(callbackContext); + } else { + regularAccuracyRequestList.add(callbackContext); + } + + if (googleApiClient != null && googleApiClient.isConnecting()) { + return; + } + } + + // Request geolocation permission. + boolean locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity().getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED; + + if (!locationPermission) { + //_saveArgs = args; + //_saveCallbackContext = callbackContext; + synchronized (semaphore) { + cordova.requestPermissions(this, callbackContext.hashCode(), new String[]{ + Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION + }); + try { + semaphore.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity().getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED; + + if (!locationPermission) { + + PluginResult errorResult = new PluginResult(PluginResult.Status.ERROR, PluginUtil.getPgmStrings(activity,"pgm_location_rejected_by_user")); + + synchronized (semaphore) { + for (CallbackContext callback: regularAccuracyRequestList) { + callback.sendPluginResult(errorResult); + } + for (CallbackContext callback: highAccuracyRequestList) { + callback.sendPluginResult(errorResult); + } + regularAccuracyRequestList.clear(); + highAccuracyRequestList.clear(); + } + return; + } + } + if (lastLocation != null && Calendar.getInstance().getTimeInMillis() - lastLocation.getTime() <= 2000) { + //--------------------------------------------------------------------- + // If the user requests the location in two seconds from the last time, + // return the last result in order to save battery usage. + // (Don't request the device location too much! Save battery usage!) + //--------------------------------------------------------------------- + JSONObject result; + try { + result = PluginUtil.location2Json(lastLocation); + result.put("status", true); + + PluginResult successResult = new PluginResult(PluginResult.Status.OK, result); + synchronized (semaphore) { + for (CallbackContext callback: regularAccuracyRequestList) { + callback.sendPluginResult(successResult); + } + for (CallbackContext callback: highAccuracyRequestList) { + callback.sendPluginResult(successResult); + } + regularAccuracyRequestList.clear(); + highAccuracyRequestList.clear(); + } + } catch (JSONException e) { + e.printStackTrace(); + } + + return; + } + + if (googleApiClient == null) { + + googleApiClient = new GoogleApiClient.Builder(activity) + .addApi(LocationServices.API) + .addConnectionCallbacks(new com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks() { + + @Override + public void onConnected(Bundle connectionHint) { + Log.d(TAG, "===> onConnected " + callbackContext.getCallbackId()); + requestLocation(); + } + + @Override + public void onConnectionSuspended(int cause) { + Log.e(TAG, "===> onConnectionSuspended"); + } + + }) + .addOnConnectionFailedListener(new com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener() { + + @Override + public void onConnectionFailed(@NonNull ConnectionResult result) { + Log.e(TAG, "===> onConnectionFailed"); + + PluginResult errorResult = new PluginResult(PluginResult.Status.ERROR, result.toString()); + + synchronized (semaphore) { + for (CallbackContext callback: regularAccuracyRequestList) { + callback.sendPluginResult(errorResult); + } + for (CallbackContext callback: highAccuracyRequestList) { + callback.sendPluginResult(errorResult); + } + regularAccuracyRequestList.clear(); + highAccuracyRequestList.clear(); + } + + googleApiClient.disconnect(); + } + + }) + .build(); + googleApiClient.connect(); + } else if (googleApiClient.isConnected()) { + requestLocation(); + } else { + Log.e(TAG, "===> googleApiClient.isConnected() is not connected"); + googleApiClient.connect(); + } + } + + private void requestLocation() { + Log.d(TAG, "--->regularAccuracyRequestList.size = " + regularAccuracyRequestList.size()); + + if (regularAccuracyRequestList.size() > 0) { + PluginLocationService.this._requestLocationUpdate(false, false, new CallbackContext("regular-callback", webView) { + @Override + public void sendPluginResult(PluginResult pluginResult) { + + synchronized (semaphore) { + for (CallbackContext callback: regularAccuracyRequestList) { + callback.sendPluginResult(pluginResult); + } + regularAccuracyRequestList.clear(); + + if (regularAccuracyRequestList.size() == 0 && highAccuracyRequestList.size() == 0) { + googleApiClient.disconnect(); + } + } + + } + }); + } + Log.d(TAG, "--->highAccuracyRequestList.size = " + highAccuracyRequestList.size()); + if (highAccuracyRequestList.size() > 0) { + PluginLocationService.this._requestLocationUpdate(false, true, new CallbackContext("regular-callback", webView) { + @Override + public void sendPluginResult(PluginResult pluginResult) { + + synchronized (semaphore) { + for (CallbackContext callback: highAccuracyRequestList) { + callback.sendPluginResult(pluginResult); + } + highAccuracyRequestList.clear(); + if (regularAccuracyRequestList.size() == 0 && highAccuracyRequestList.size() == 0) { + googleApiClient.disconnect(); + } + } + + } + }); + } + } + +// private void _checkLocationSettings() { +// +// LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().setAlwaysShow(true); +// +// LocationRequest locationRequest; +// locationRequest = LocationRequest.create() +// .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); +// builder.addLocationRequest(locationRequest); +// +// if (enableHighAccuracy) { +// locationRequest = LocationRequest.create() +// .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); +// builder.addLocationRequest(locationRequest); +// } +// +// PendingResult locationSettingsResult = +// LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build()); +// +// locationSettingsResult.setResultCallback(new ResultCallback() { +// +// @Override +// public void onResult(@NonNull LocationSettingsResult result) { +// final Status status = result.getStatus(); +// switch (status.getStatusCode()) { +// case LocationSettingsStatusCodes.SUCCESS: +// _requestLocationUpdate(false, enableHighAccuracy, callbackContext); +// break; +// +// case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: +// // Location settings are not satisfied. But could be fixed by showing the user +// // a dialog. +// try { +// //Keep the callback id +// Bundle bundle = new Bundle(); +// bundle.putInt("type", ACTIVITY_LOCATION_DIALOG); +// bundle.putString("callbackId", callbackContext.getCallbackId()); +// bundle.putBoolean("enableHighAccuracy", enableHighAccuracy); +// int hashCode = bundle.hashCode(); +// +// bufferForLocationDialog.put("bundle_" + hashCode, bundle); +// //PluginLocationService.this.sendNoResult(callbackContext); +// +// // Show the dialog by calling startResolutionForResult(), +// // and check the result in onActivityResult(). +// cordova.setActivityResultCallback(PluginLocationService.this); +// status.startResolutionForResult(cordova.getActivity(), hashCode); +// } catch (IntentSender.SendIntentException e) { +// // Show the dialog that is original version of this plugin. +// _showLocationSettingsPage(enableHighAccuracy, callbackContext); +// } +// break; +// +// case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: +// // Location settings are not satisfied. However, we have no way to fix the +// // settings so we won't show the dialog. +// +// JSONObject jsResult = new JSONObject(); +// try { +// jsResult.put("status", false); +// jsResult.put("error_code", "service_not_available"); +// jsResult.put("error_message", "This app has been rejected to use Location Services."); +// } catch (JSONException e) { +// e.printStackTrace(); +// } +// callbackContext.error(jsResult); +// break; +// } +// } +// +// }); +// } +// +// private void _showLocationSettingsPage(final boolean enableHighAccuracy, final CallbackContext callbackContext) { +// //Ask the user to turn on the location services. +// AlertDialog.Builder builder = new AlertDialog.Builder(this.activity); +// builder.setTitle("Improve location accuracy"); +// builder.setMessage("To enhance your Maps experience:\n\n" + +// " - Enable Google apps location access\n\n" + +// " - Turn on GPS and mobile network location"); +// builder.setPositiveButton("Settings", new DialogInterface.OnClickListener() { +// @Override +// public void onClick(DialogInterface dialog, int which) { +// //Keep the callback id +// Bundle bundle = new Bundle(); +// bundle.putInt("type", ACTIVITY_LOCATION_PAGE); +// bundle.putString("callbackId", callbackContext.getCallbackId()); +// bundle.putBoolean("enableHighAccuracy", enableHighAccuracy); +// int hashCode = bundle.hashCode(); +// +// bufferForLocationDialog.put("bundle_" + hashCode, bundle); +// //PluginLocationService.this.sendNoResult(callbackContext); +// +// //Launch settings, allowing user to make a change +// cordova.setActivityResultCallback(PluginLocationService.this); +// Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); +// activity.startActivityForResult(intent, hashCode); +// } +// }); +// builder.setNegativeButton("Skip", new DialogInterface.OnClickListener() { +// @Override +// public void onClick(DialogInterface dialog, int which) { +// //No location service, no Activity +// dialog.dismiss(); +// +// JSONObject result = new JSONObject(); +// try { +// result.put("status", false); +// result.put("error_code", "service_denied"); +// result.put("error_message", "This app has been rejected to use Location Services."); +// } catch (JSONException e) { +// e.printStackTrace(); +// } +// callbackContext.error(result); +// } +// }); +// builder.create().show(); +// } + + @SuppressWarnings("MissingPermission") + private void _requestLocationUpdate(final boolean isRetry, final boolean enableHighAccuracy, final CallbackContext callbackContext) { + + Log.d(TAG, "---->_requestLocationUpdate (isRetry = " + isRetry + ")"); + int priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; + if (enableHighAccuracy || "Genymotion".equals(Build.MANUFACTURER)) { + priority = LocationRequest.PRIORITY_HIGH_ACCURACY; + } + + if (!isRetry) { + LocationServices.getFusedLocationProviderClient(cordova.getActivity()) + .getLastLocation() + .addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(Location location) { + lastLocation = location; + if (lastLocation != null && Calendar.getInstance().getTimeInMillis() - lastLocation.getTime() <= 2000) { + Log.d(TAG, "---->The last location is obtained in 2 sec."); + //--------------------------------------------------------------------- + // If the user requests the location in two seconds from the last time, + // return the last result in order to save battery usage. + // (Don't request the device location too much! Save battery usage!) + //--------------------------------------------------------------------- + JSONObject result; + try { + result = PluginUtil.location2Json(lastLocation); + result.put("status", true); + + PluginResult successResult = new PluginResult(PluginResult.Status.OK, result); + synchronized (semaphore) { + for (CallbackContext callback : regularAccuracyRequestList) { + callback.sendPluginResult(successResult); + } + for (CallbackContext callback: highAccuracyRequestList) { + callback.sendPluginResult(successResult); + } + regularAccuracyRequestList.clear(); + highAccuracyRequestList.clear(); + } + } catch (JSONException e) { + e.printStackTrace(); + } + + googleApiClient.disconnect(); + } else { + Log.d(TAG, "---->The last location is expired. Let's get the latest location..."); + _requestLocationUpdate(true, enableHighAccuracy, callbackContext); + } + } + }) + .addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + _requestLocationUpdate(true, enableHighAccuracy, callbackContext); + } + }); + return; + } + + // It seems setting the values to both setFastestInterval() and setInterval() is important. + // https://akira-watson.com/android/fusedlocationproviderapi.html + LocationRequest locationRequest= LocationRequest.create() + //.setNumUpdates(2) + .setFastestInterval(5000) + .setInterval(10000) + //.setSmallestDisplacement(0) + .setPriority(priority) + .setExpirationDuration(12000) + .setMaxWaitTime(5000); + + LocationServices.getFusedLocationProviderClient(cordova.getActivity()).requestLocationUpdates(locationRequest, new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + Log.d(TAG, "---->onLocationResult"); + Location location = null; + if (locationResult.getLocations().size() > 0) { + location = locationResult.getLocations().get(0); + lastLocation = location; + } else if (locationResult.getLastLocation() != null) { + location = locationResult.getLastLocation(); + } + + Log.d(TAG, "===> location =" + location); + if (location != null) { + JSONObject result; + Log.d(TAG, "===> location =" + callbackContext.getCallbackId()); + try { + result = PluginUtil.location2Json(location); + result.put("status", true); + callbackContext.success(result); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + // Send back the error result + JSONObject result = new JSONObject(); + try { + result.put("status", false); + result.put("error_code", "cannot_detect"); + result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_can_not_get_location")); + } catch (JSONException e) { + e.printStackTrace(); + } + callbackContext.error(result); + } + + googleApiClient.disconnect(); + } + }, Looper.myLooper()); + + } + + private void _onActivityResultLocationPage(Bundle bundle) { + String callbackId = bundle.getString("callbackId"); + CallbackContext callbackContext = new CallbackContext(callbackId, this.webView); + + LocationManager locationManager = (LocationManager) this.activity.getSystemService(Context.LOCATION_SERVICE); + List providers = locationManager.getAllProviders(); + int availableProviders = 0; + //if (mPluginLayout != null && mPluginLayout.isDebug) { + Log.d(TAG, "---debug at getMyLocation(available providers)--"); + //} + Iterator iterator = providers.iterator(); + String provider; + boolean isAvailable; + while(iterator.hasNext()) { + provider = iterator.next(); + if ("passive".equals(provider)) { + continue; + } + isAvailable = locationManager.isProviderEnabled(provider); + if (isAvailable) { + availableProviders++; + } + //if (mPluginLayout != null && mPluginLayout.isDebug) { + Log.d(TAG, " " + provider + " = " + (isAvailable ? "" : "not ") + "available"); + //} + } + if (availableProviders == 0) { + JSONObject result = new JSONObject(); + try { + result.put("status", false); + result.put("error_code", "not_available"); + result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_no_location_providers")); + } catch (JSONException e) { + e.printStackTrace(); + } + callbackContext.error(result); + return; + } + + _inviteLocationUpdateAfterActivityResult(bundle); + } + + private void _inviteLocationUpdateAfterActivityResult(Bundle bundle) { + boolean enableHighAccuracy = bundle.getBoolean("enableHighAccuracy"); + String callbackId = bundle.getString("callbackId"); + CallbackContext callbackContext = new CallbackContext(callbackId, this.webView); + this._requestLocationUpdate(false, enableHighAccuracy, callbackContext); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (!bufferForLocationDialog.containsKey("bundle_" + requestCode)) { + Log.e(TAG, "no key"); + return; + } + Bundle query = bufferForLocationDialog.get("bundle_" + requestCode); + Log.d(TAG, "====> onActivityResult (" + resultCode + ")"); + + switch (query.getInt("type")) { + case ACTIVITY_LOCATION_DIALOG: + // User was asked to enable the location setting. + switch (resultCode) { + case Activity.RESULT_OK: + // All required changes were successfully made + _inviteLocationUpdateAfterActivityResult(query); + break; + case Activity.RESULT_CANCELED: + // The user was asked to change settings, but chose not to + _userRefusedToUseLocationAfterActivityResult(query); + break; + default: + break; + } + break; + case ACTIVITY_LOCATION_PAGE: + _onActivityResultLocationPage(query); + break; + } + } + private void _userRefusedToUseLocationAfterActivityResult(Bundle bundle) { + String callbackId = bundle.getString("callbackId"); + CallbackContext callbackContext = new CallbackContext(callbackId, this.webView); + JSONObject result = new JSONObject(); + try { + result.put("status", false); + result.put("error_code", "service_denied"); + result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_location_rejected_by_user")); + } catch (JSONException e) { + e.printStackTrace(); + } + callbackContext.error(result); + } + + public void onRequestPermissionResult(int requestCode, String[] permissions, + int[] grantResults) throws JSONException { + synchronized (semaphore) { + semaphore.notify(); + } + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMap.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMap.java new file mode 100644 index 0000000..a3d379f --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMap.java @@ -0,0 +1,3183 @@ +package plugin.google.maps; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.Point; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.location.Location; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.v4.content.PermissionChecker; +import android.util.Base64; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.google.android.gms.maps.CameraUpdate; +import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.GoogleMap.InfoWindowAdapter; +import com.google.android.gms.maps.GoogleMap.OnIndoorStateChangeListener; +import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener; +import com.google.android.gms.maps.GoogleMap.OnMapClickListener; +import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener; +import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener; +import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener; +import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener; +import com.google.android.gms.maps.GoogleMapOptions; +import com.google.android.gms.maps.MapView; +import com.google.android.gms.maps.OnMapReadyCallback; +import com.google.android.gms.maps.Projection; +import com.google.android.gms.maps.UiSettings; +import com.google.android.gms.maps.model.CameraPosition; +import com.google.android.gms.maps.model.CameraPosition.Builder; +import com.google.android.gms.maps.model.Circle; +import com.google.android.gms.maps.model.GroundOverlay; +import com.google.android.gms.maps.model.IndoorBuilding; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; +import com.google.android.gms.maps.model.MapStyleOptions; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.MarkerOptions; +import com.google.android.gms.maps.model.PointOfInterest; +import com.google.android.gms.maps.model.Polygon; +import com.google.android.gms.maps.model.Polyline; +import com.google.android.gms.maps.model.PolylineOptions; +import com.google.android.gms.maps.model.VisibleRegion; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginEntry; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + + +public class PluginMap extends MyPlugin implements OnMarkerClickListener, + OnInfoWindowClickListener, OnMapClickListener, OnMapLongClickListener, + OnMarkerDragListener, GoogleMap.OnMapLoadedCallback, + OnMyLocationButtonClickListener, OnIndoorStateChangeListener, InfoWindowAdapter, + GoogleMap.OnCameraIdleListener, GoogleMap.OnCameraMoveCanceledListener, + GoogleMap.OnCameraMoveListener, GoogleMap.OnCameraMoveStartedListener, + GoogleMap.OnInfoWindowLongClickListener, GoogleMap.OnInfoWindowCloseListener, + GoogleMap.OnMyLocationClickListener, GoogleMap.OnPoiClickListener, + IPluginView{ + + private LatLngBounds initCameraBounds; + private Activity activity; + public GoogleMap map; + private MapView mapView; + private String mapId; + private boolean isVisible = true; + private boolean isClickable = true; + private final String TAG = mapId; + private String mapDivId; + public Map plugins = new ConcurrentHashMap(); + private final float DEFAULT_CAMERA_PADDING = 20; + private Projection projection = null; + public Marker activeMarker = null; + private boolean isDragging = false; + public final ObjectCache objects = new ObjectCache(); + private ImageView dummyMyLocationButton; + public static final Object semaphore = new Object(); + private int viewDepth = 0; + + private enum TEXT_STYLE_ALIGNMENTS { + left, center, right + } + + private final String ANIMATE_CAMERA_DONE = "animate_camera_done"; + private final String ANIMATE_CAMERA_CANCELED = "animate_camera_canceled"; + + private Handler mainHandler; + + private class AsyncUpdateCameraPositionResult { + CameraUpdate cameraUpdate; + int durationMS; + LatLngBounds cameraBounds; + double cameraPadding; + } + + private class AsyncSetOptionsResult { + int MAP_TYPE_ID; + CameraPosition cameraPosition; + LatLngBounds cameraBounds; + double cameraPadding; + String styles; + } + + public int getViewDepth() { + return viewDepth; + } + public String getDivId() { + return this.mapDivId; + } + public String getOverlayId() { + return this.mapId; + } + public ViewGroup getView() { + return this.mapView; + } + public boolean getVisible() { + return isVisible; + } + public boolean getClickable() { + return isClickable; + } + + + @Override + public void initialize(CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + activity = cordova.getActivity(); + mainHandler = new Handler(Looper.getMainLooper()); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void getMap(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + GoogleMapOptions options = new GoogleMapOptions(); + JSONObject meta = args.getJSONObject(0); + mapId = meta.getString("__pgmId"); + viewDepth = meta.getInt("depth"); + final JSONObject params = args.getJSONObject(1); + + //controls + if (params.has("controls")) { + JSONObject controls = params.getJSONObject("controls"); + + if (controls.has("compass")) { + options.compassEnabled(controls.getBoolean("compass")); + } + if (controls.has("zoom")) { + options.zoomControlsEnabled(controls.getBoolean("zoom")); + } + if (controls.has("mapToolbar")) { + options.mapToolbarEnabled(controls.getBoolean("mapToolbar")); + } + + + if (controls.has("myLocationButton") || controls.has("myLocation")) { + + // Request geolocation permission. + boolean locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED; + //Log.d(TAG, "---> (235) hasPermission = " + locationPermission); + + if (!locationPermission) { + //_saveArgs = args; + //_saveCallbackContext = callbackContext; + synchronized (semaphore) { + cordova.requestPermissions(PluginMap.this, callbackContext.hashCode(), new String[]{ + Manifest.permission.ACCESS_FINE_LOCATION + }); + try { + semaphore.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_GRANTED; + + //Log.d(TAG, "---> (252)setMyLocationEnabled, hasPermission = " + locationPermission); + + } + } + } + + //gestures + if (params.has("gestures")) { + JSONObject gestures = params.getJSONObject("gestures"); + + if (gestures.has("tilt")) { + options.tiltGesturesEnabled(gestures.getBoolean("tilt")); + } + if (gestures.has("scroll")) { + options.scrollGesturesEnabled(gestures.getBoolean("scroll")); + } + if (gestures.has("rotate")) { + options.rotateGesturesEnabled(gestures.getBoolean("rotate")); + } + if (gestures.has("zoom")) { + options.zoomGesturesEnabled(gestures.getBoolean("zoom")); + } + } + + // map type + if (!params.has("styles") && params.has("mapType")) { + String typeStr = params.getString("mapType"); + int mapTypeId = -1; + mapTypeId = typeStr.equals("MAP_TYPE_NORMAL") ? GoogleMap.MAP_TYPE_NORMAL : mapTypeId; + mapTypeId = typeStr.equals("MAP_TYPE_HYBRID") ? GoogleMap.MAP_TYPE_HYBRID : mapTypeId; + mapTypeId = typeStr.equals("MAP_TYPE_SATELLITE") ? GoogleMap.MAP_TYPE_SATELLITE : mapTypeId; + mapTypeId = typeStr.equals("MAP_TYPE_TERRAIN") ? GoogleMap.MAP_TYPE_TERRAIN : mapTypeId; + mapTypeId = typeStr.equals("MAP_TYPE_NONE") ? GoogleMap.MAP_TYPE_NONE : mapTypeId; + if (mapTypeId != -1) { + options.mapType(mapTypeId); + } + } + + // initial camera position + if (params.has("camera")) { + JSONObject camera = params.getJSONObject("camera"); + Builder builder = CameraPosition.builder(); + if (camera.has("bearing")) { + builder.bearing((float) camera.getDouble("bearing")); + } + if (camera.has("target")) { + Object target = camera.get("target"); + @SuppressWarnings("rawtypes") + Class targetClass = target.getClass(); + if ("org.json.JSONArray".equals(targetClass.getName())) { + JSONArray points = camera.getJSONArray("target"); + initCameraBounds = PluginUtil.JSONArray2LatLngBounds(points); + builder.target(initCameraBounds.getCenter()); + + } else { + JSONObject latLng = camera.getJSONObject("target"); + builder.target(new LatLng(latLng.getDouble("lat"), latLng.getDouble("lng"))); + if (camera.has("zoom")) { + builder.zoom((float) camera.getDouble("zoom")); + } + } + } else { + builder.target(new LatLng(0, 0)); + } + if (camera.has("tilt")) { + builder.tilt((float) camera.getDouble("tilt")); + } + options.camera(builder.build()); + } + + mapView = new MapView(activity, options); + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + mapView.onCreate(null); + mapView.setTag(getViewDepth()); + + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(GoogleMap googleMap) { + + dummyMyLocationButton = new ImageView(activity); + FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams((int)(48 * density), (int)(48 * density)); + lParams.gravity = Gravity.RIGHT; + lParams.rightMargin = (int)(6 * density); + lParams.topMargin = (int)(6 * density); + lParams.leftMargin = 0; + dummyMyLocationButton.setClickable(true); + dummyMyLocationButton.setAlpha(0.75f); + dummyMyLocationButton.setVisibility(View.GONE); + dummyMyLocationButton.setLayoutParams(lParams); + + int buttonImgId = PluginUtil.getAppResource(cordova.getActivity(), "dummy_my_location_button", "drawable"); + dummyMyLocationButton.setImageBitmap(BitmapFactory.decodeResource(activity.getResources(), buttonImgId)); + + int shadowXmlId = PluginUtil.getAppResource(cordova.getActivity(), "dummy_mylocation_button_shadow", "drawable"); + dummyMyLocationButton.setBackground(activity.getResources().getDrawable(shadowXmlId)); + + dummyMyLocationButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PluginMap.this.onMyLocationButtonClick(); + } + }); + mapView.addView(dummyMyLocationButton); + + map = googleMap; + projection = map.getProjection(); + + try { + //styles + if (params.has("styles")) { + String styles = params.getString("styles"); + MapStyleOptions styleOptions = new MapStyleOptions(styles); + map.setMapStyle(styleOptions); + map.setMapType(GoogleMap.MAP_TYPE_NORMAL); + } + + //controls + if (params.has("controls")) { + JSONObject controls = params.getJSONObject("controls"); + + if (controls.has("indoorPicker")) { + Boolean isEnabled = controls.getBoolean("indoorPicker"); + map.setIndoorEnabled(isEnabled); + } + + if (controls.has("myLocationButton") || controls.has("myLocation")) { + boolean locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED; + //Log.d(TAG, "---> (314) hasPermission = " + locationPermission); + + if (locationPermission) { + Boolean isMyLocationEnabled = false; + if (controls.has("myLocation")) { + isMyLocationEnabled = controls.getBoolean("myLocation"); + map.setMyLocationEnabled(isMyLocationEnabled); + } + + Boolean isMyLocationButtonEnabled = false; + if (controls.has("myLocationButton")) { + isMyLocationButtonEnabled = controls.getBoolean("myLocationButton"); + map.getUiSettings().setMyLocationButtonEnabled(isMyLocationButtonEnabled); + } + //Log.d(TAG, "--->isMyLocationButtonEnabled = " + isMyLocationButtonEnabled + ", isMyLocationEnabled = " + isMyLocationEnabled); + if (!isMyLocationEnabled && isMyLocationButtonEnabled) { + dummyMyLocationButton.setVisibility(View.VISIBLE); + } else { + dummyMyLocationButton.setVisibility(View.GONE); + } + } + } + } + //preferences + if (params.has("preferences")) { + JSONObject preferences = params.getJSONObject("preferences"); + + if (preferences.has("padding")) { + JSONObject padding = preferences.getJSONObject("padding"); + int left = 0, top = 0, bottom = 0, right = 0; + if (padding.has("left")) { + left = (int) (padding.getInt("left") * density); + } + if (padding.has("top")) { + top = (int) (padding.getInt("top") * density); + } + if (padding.has("bottom")) { + bottom = (int) (padding.getInt("bottom") * density); + } + if (padding.has("right")) { + right = (int) (padding.getInt("right") * density); + } + map.setPadding(left, top, right, bottom); + + FrameLayout.LayoutParams lParams2 = (FrameLayout.LayoutParams) dummyMyLocationButton.getLayoutParams(); + lParams2.rightMargin = right + (int)(5 * density); + lParams2.topMargin = top + (int)(5 * density); + dummyMyLocationButton.setLayoutParams(lParams2); + + } + + if (preferences.has("zoom")) { + JSONObject zoom = preferences.getJSONObject("zoom"); + if (zoom.has("minZoom")) { + map.setMinZoomPreference((float)zoom.getDouble("minZoom")); + } + if (zoom.has("maxZoom")) { + map.setMaxZoomPreference((float)zoom.getDouble("maxZoom")); + } + } + + + if (preferences.has("gestureBounds")) { + Object target = preferences.get("gestureBounds"); + @SuppressWarnings("rawtypes") + Class targetClass = target.getClass(); + if ("org.json.JSONArray".equals(targetClass.getName())) { + JSONArray points = preferences.getJSONArray("gestureBounds"); + LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points); + map.setLatLngBoundsForCameraTarget(bounds); + } + } + } + + // Set event listener + map.setOnCameraIdleListener(PluginMap.this); + map.setOnCameraMoveCanceledListener(PluginMap.this); + map.setOnCameraMoveListener(PluginMap.this); + map.setOnCameraMoveStartedListener(PluginMap.this); + map.setOnMapClickListener(PluginMap.this); + map.setOnMapLongClickListener(PluginMap.this); + map.setOnMarkerClickListener(PluginMap.this); + map.setOnMarkerDragListener(PluginMap.this); + map.setOnMyLocationButtonClickListener(PluginMap.this); + map.setOnMapLoadedCallback(PluginMap.this); + map.setOnIndoorStateChangeListener(PluginMap.this); + map.setOnInfoWindowClickListener(PluginMap.this); + map.setOnInfoWindowLongClickListener(PluginMap.this); + map.setOnInfoWindowCloseListener(PluginMap.this); + map.setOnMyLocationClickListener(PluginMap.this); + map.setOnPoiClickListener(PluginMap.this); + + //Custom info window + map.setInfoWindowAdapter(PluginMap.this); + + + mapView.onResume(); + + + // ------------------------------ + // Embed the map if a container is specified. + // ------------------------------ + if (args.length() == 3) { + mapDivId = args.getString(2); + + mapCtrl.mPluginLayout.addPluginOverlay(PluginMap.this); + PluginMap.this.resizeMap(args, new PluginUtil.MyCallbackContext("dummy-" + map.hashCode(), webView) { + @Override + public void onResult(PluginResult pluginResult) { + + if (initCameraBounds != null) { + map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { + @Override + public void onCameraIdle() { + mapView.setVisibility(View.INVISIBLE); + PluginMap.this.onCameraIdle(); + map.setOnCameraIdleListener(PluginMap.this); + Handler handler = new Handler(); + handler.postDelayed(new AdjustInitCamera(params, callbackContext), 750); + } + }); + } else { + mapView.setVisibility(View.VISIBLE); + PluginMap.this.onCameraEvent("camera_move_end"); + callbackContext.success(); + } + } + }); + } else { + if (initCameraBounds != null) { + map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { + @Override + public void onCameraIdle() { + PluginMap.this.onCameraIdle(); + map.setOnCameraIdleListener(PluginMap.this); + mapView.setVisibility(View.INVISIBLE); + Handler handler = new Handler(); + handler.postDelayed(new AdjustInitCamera(params, callbackContext), 750); + } + }); + } else { + mapView.setVisibility(View.VISIBLE); + PluginMap.this.onCameraEvent("camera_move_end"); + callbackContext.success(); + //if (map.getMapType() == GoogleMap.MAP_TYPE_NONE) { + PluginMap.this.onMapLoaded(); + //} + } + } + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + } + }); + + + } + }); + } + + @Override + public void onStart() { + super.onStart(); + if (mapView != null) { + mapView.onStart(); + } + } + + @Override + public void onStop() { + super.onStop(); + if (mapView != null) { + mapView.onStop(); + } + } + + @Override + public void onPause(boolean multitasking) { + super.onPause(multitasking); + if (mapView != null && mapView.isActivated()) { + mapView.onPause(); + } + + //mapCtrl.mPluginLayout.removePluginOverlay(this.mapId); + + } + @Override + public void onResume(boolean multitasking) { + super.onResume(multitasking); + if (mapView != null && mapView.isActivated()) { + mapView.onResume(); + } + //mapCtrl.mPluginLayout.addPluginOverlay(PluginMap.this); + } + + private class AdjustInitCamera implements Runnable { + private JSONObject mParams; + private CallbackContext mCallback; + public AdjustInitCamera(JSONObject params, CallbackContext callbackContext) { + mParams = params; + mCallback = callbackContext; + } + @Override + public void run() { + + double CAMERA_PADDING = DEFAULT_CAMERA_PADDING; + try { + if (mParams.has("camera")) { + JSONObject camera = mParams.getJSONObject("camera"); + if (camera.has("padding")) { + CAMERA_PADDING = camera.getDouble("padding"); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + map.moveCamera(CameraUpdateFactory.newLatLngBounds(initCameraBounds, (int) (CAMERA_PADDING * density))); + + CameraPosition.Builder builder = CameraPosition.builder(map.getCameraPosition()); + + try { + if (mParams.has("camera")) { + Boolean additionalParams = false; + JSONObject camera = mParams.getJSONObject("camera"); + if (camera.has("bearing")) { + builder.bearing((float) camera.getDouble("bearing")); + additionalParams = true; + } + if (camera.has("tilt")) { + builder.tilt((float) camera.getDouble("tilt")); + additionalParams = true; + } + if (additionalParams) { + map.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build())); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + mapView.setVisibility(View.VISIBLE); + mCallback.success(); + + //if (map.getMapType() == GoogleMap.MAP_TYPE_NONE) { + PluginMap.this.onMapLoaded(); + //} + + //fitBounds(initCameraBounds, CAMERA_PADDING); + } + } + + //----------------------------------- + // Create the instance of class + //----------------------------------- + public synchronized void loadPlugin(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String serviceName = args.getString(0); + final String pluginName = mapId + "-" + serviceName.toLowerCase(); + //Log.d("PluginMap", "serviceName = " + serviceName + ", pluginName = " + pluginName); + + try { + + if (plugins.containsKey(pluginName)) { + //Log.d("PluginMap", "--> useCache"); + MyPlugin myPlugin = (MyPlugin) plugins.get(pluginName).plugin; + myPlugin.create(args, callbackContext); + return; + } + + //Log.d("PluginMap", "--> create new instance"); + String className = "plugin.google.maps.Plugin" + serviceName; + Class pluginCls = Class.forName(className); + + CordovaPlugin plugin = (CordovaPlugin) pluginCls.newInstance(); + PluginEntry pluginEntry = new PluginEntry(pluginName, plugin); + plugins.put(pluginName, pluginEntry); + mapCtrl.pluginManager.addService(pluginEntry); + + plugin.privateInitialize(pluginName, cordova, webView, null); + + plugin.initialize(cordova, webView); + ((MyPluginInterface)plugin).setPluginMap(PluginMap.this); + MyPlugin myPlugin = (MyPlugin) plugin; + myPlugin.self = (MyPlugin)plugin; + myPlugin.create(args, callbackContext); + } catch (Exception e) { + e.printStackTrace(); + } + } + private void fitBounds(final LatLngBounds cameraBounds, int padding) { + Builder builder = CameraPosition.builder(); + builder.tilt(map.getCameraPosition().tilt); + builder.bearing(map.getCameraPosition().bearing); + Log.d(TAG, mapView.getWidth() + "x" + mapView.getHeight()); + + // Fit the camera to the cameraBounds with 20px padding. + CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(cameraBounds, padding / (int)density); + try { + map.moveCamera(cameraUpdate); + builder.zoom(map.getCameraPosition().zoom); + builder.target(map.getCameraPosition().target); + map.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build())); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + //----------------------------------- + // Create the instance of class + //----------------------------------- + @SuppressWarnings("rawtypes") + public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String className = args.getString(0); + + + try { + if (plugins.containsKey(className)) { + PluginEntry pluginEntry = plugins.get(className); + pluginEntry.plugin.execute("create", args, callbackContext); + return; + } + + Class pluginCls = Class.forName("plugin.google.maps.Plugin" + className); + + CordovaPlugin plugin = (CordovaPlugin) pluginCls.newInstance(); + PluginEntry pluginEntry = new PluginEntry(mapId + "-" + className, plugin); + plugins.put(className, pluginEntry); + pluginMap = PluginMap.this; + pluginMap.mapCtrl.pluginManager.addService(pluginEntry); + + plugin.privateInitialize(className, cordova, webView, null); + plugin.initialize(cordova, webView); + ((MyPluginInterface)plugin).setPluginMap(PluginMap.this); + pluginEntry.plugin.execute("create", args, callbackContext); + + + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public void attachToWebView(JSONArray args, final CallbackContext callbackContext) { + mapCtrl.mPluginLayout.addPluginOverlay(this); + callbackContext.success(); + } + public void detachFromWebView(JSONArray args, final CallbackContext callbackContext) { + mapCtrl.mPluginLayout.removePluginOverlay(this.mapId); + callbackContext.success(); + } + + public void resizeMap(JSONArray args, final CallbackContext callbackContext) throws JSONException { + if (mapCtrl.mPluginLayout == null || mapDivId == null) { + //Log.d("PluginMap", "---> resizeMap / mPluginLayout = null"); + callbackContext.success(); + if (initCameraBounds != null) { + mainHandler.postDelayed(new Runnable() { + @Override + public void run() { + } + }, 100); + } + return; + } + + mapCtrl.mPluginLayout.needUpdatePosition = true; + + if (!mapCtrl.mPluginLayout.HTMLNodes.containsKey(mapDivId)) { + Bundle dummyInfo = new Bundle(); + dummyInfo.putBoolean("isDummy", true); + dummyInfo.putDouble("offsetX", 0); + dummyInfo.putDouble("offsetY", 3000); + + Bundle dummySize = new Bundle(); + dummySize.putDouble("left", 0); + dummySize.putDouble("top", 3000); + dummySize.putDouble("width", 200); + dummySize.putDouble("height", 200); + dummyInfo.putBundle("size", dummySize); + dummySize.putDouble("depth", -999); + mapCtrl.mPluginLayout.HTMLNodes.put(mapDivId, dummyInfo); + } + + + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + if(mapCtrl.mPluginLayout == null || mapDivId == null) { + callbackContext.success(); + return; + } + + RectF drawRect = mapCtrl.mPluginLayout.HTMLNodeRectFs.get(mapDivId); + + //Log.d(TAG, "--->mapDivId = " + mapDivId + ", drawRect = " + drawRect); + if (drawRect != null) { + final int scrollY = webView.getView().getScrollY(); + + int width = (int) drawRect.width(); + int height = (int) drawRect.height(); + int x = (int) drawRect.left; + int y = (int) drawRect.top + scrollY; + ViewGroup.LayoutParams lParams = mapView.getLayoutParams(); + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) lParams; + + params.width = width; + params.height = height; + params.leftMargin = x; + params.topMargin = y; + mapView.setLayoutParams(params); + + callbackContext.success(); + } + } + }); + } + + public void setDiv(JSONArray args, CallbackContext callbackContext) throws JSONException { + if (args.length() == 0) { + PluginMap.this.mapDivId = null; + mapCtrl.mPluginLayout.removePluginOverlay(mapId); + callbackContext.success(); + return; + } + PluginMap.this.mapDivId = args.getString(0); + mapCtrl.mPluginLayout.addPluginOverlay(PluginMap.this); + this.resizeMap(args, callbackContext); + } + + /** + * Set clickable of the map + * @param args Parameters given from JavaScript side + * @param callbackContext Callback contect for sending back the result. + * @throws JSONException + */ + public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException { + boolean clickable = args.getBoolean(0); + this.isClickable = clickable; + //mapCtrl.mPluginLayout.setClickable(mapId, clickable); + callbackContext.success(); + } + + /** + * Set visibility of the map + * @param args Parameters given from JavaScript side + * @param callbackContext Callback contect for sending back the result. + * @throws JSONException + */ + public void setVisible(JSONArray args, final CallbackContext callbackContext) throws JSONException { + final boolean visible = args.getBoolean(0); + this.isVisible = visible; + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (visible) { + mapView.setVisibility(View.VISIBLE); + } else { + mapView.setVisibility(View.INVISIBLE); + } + callbackContext.success(); + } + }); + } + + @Override + public void onDestroy() { + super.onDestroy(); + this.objects.clear(); + this.objects.destroy(); + } + + /** + * Destroy the map completely + * @param args Parameters given from JavaScript side + * @param callbackContext Callback contect for sending back the result. + */ + public void remove(JSONArray args, final CallbackContext callbackContext) { + this.isClickable = false; + this.isRemoved = true; + + try { + PluginMap.this.clear(null, new PluginUtil.MyCallbackContext(mapId + "_remove", webView) { + + @Override + public void onResult(PluginResult pluginResult) { + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mapCtrl.mPluginLayout.removePluginOverlay(mapId); + + //Log.d("pluginMap", "--> map = " + map); + if (map != null) { + try { + map.setIndoorEnabled(false); + map.setMyLocationEnabled(false); + map.setOnPolylineClickListener(null); + map.setOnPolygonClickListener(null); + map.setOnIndoorStateChangeListener(null); + map.setOnCircleClickListener(null); + map.setOnGroundOverlayClickListener(null); + map.setOnCameraIdleListener(null); + map.setOnCameraMoveCanceledListener(null); + map.setOnCameraMoveListener(null); + map.setOnInfoWindowClickListener(null); + map.setOnInfoWindowCloseListener(null); + map.setOnMapClickListener(null); + map.setOnMapLongClickListener(null); + map.setOnMarkerClickListener(null); + map.setOnMyLocationButtonClickListener(null); + map.setOnMapLoadedCallback(null); + map.setOnMarkerDragListener(null); + map.setOnMyLocationClickListener(null); + map.setOnPoiClickListener(null); + } catch (SecurityException e) { + e.printStackTrace(); + } + } + if (mapView != null) { + try { + mapView.clearAnimation(); + //mapView.onCancelPendingInputEvents(); // Android 4.2 crashes + mapView.onPause(); + mapView.onDestroy(); + //Log.d("pluginMap", "--> mapView.onDestroy()"); + } catch (Exception e) { + e.printStackTrace(); + } + } + if (plugins.size() > 0) { + String[] pluginNames = plugins.keySet().toArray(new String[plugins.size()]); + PluginEntry pluginEntry; + for (int i = 0; i < pluginNames.length; i++) { + pluginEntry = plugins.remove(pluginNames[i]); + if (pluginEntry == null) { + continue; + } + pluginEntry.plugin.onDestroy(); + ((MyPlugin)pluginEntry.plugin).map = null; + ((MyPlugin)pluginEntry.plugin).mapCtrl = null; + //((MyPlugin)pluginEntry.plugin).pluginMap = null; // Do not clear at here. + pluginEntry = null; + } + } + //Log.d("pluginMap", "--> mapView = " + mapView); + projection = null; + plugins = null; + map = null; + mapView = null; + initCameraBounds = null; + activity = null; + mapId = null; + mapDivId = null; + activeMarker = null; + + System.gc(); + Runtime.getRuntime().gc(); + if (callbackContext != null) { + callbackContext.success(); + } + PluginMap.this.onDestroy(); + } + }); + } + }); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + @Override + public View getInfoContents(Marker marker) { + //Log.d(TAG, "--->getInfoContents"); + activeMarker = marker; + String title = marker.getTitle(); + String snippet = marker.getSnippet(); + if ((title == null) && (snippet == null)) { + return null; + } + + String markerTag = (String) marker.getTag(); + String tmp[] = markerTag.split("_"); + String className = tmp[0]; + tmp = markerTag.split("-"); + String markerId = tmp[tmp.length - 1]; + + PluginEntry pluginEntry = plugins.get(mapId + "-" + className); + if (pluginEntry == null) { + //Log.d(TAG, "---> getInfoContents / marker.title = " + marker.getTitle()); + return null; + } + MyPlugin myPlugin = (MyPlugin)pluginEntry.plugin; + + JSONObject properties = null; + JSONObject styles = null; + String propertyId = "marker_property_" + markerTag; + //Log.d(TAG, "---> getInfoContents / propertyId = " + propertyId); + + if (objects.containsKey(propertyId)) { + properties = (JSONObject) objects.get(propertyId); + + try { + if (properties.has("styles")) { + styles = (JSONObject) properties.getJSONObject("styles"); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + if ((marker.getTag() + "").startsWith("markercluster_")){ + this.onClusterEvent("info_open", marker); + } else { + this.onMarkerEvent("info_open", marker); + } + + // Linear layout + LinearLayout windowLayer = new LinearLayout(activity); + windowLayer.setPadding(3, 3, 3, 3); + windowLayer.setOrientation(LinearLayout.VERTICAL); + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); + layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER; + + int maxWidth = 0; + + if (styles != null) { + if (styles.has("width")) { + try { + int width = 0; + String widthString = styles.getString("width"); + + if (widthString.endsWith("%")) { + double widthDouble = Double.parseDouble(widthString.replace("%", "")); + + width = (int) ((double) mapView.getWidth() * (widthDouble / 100)); + } else if (PluginUtil.isNumeric(widthString)) { + double widthDouble = Double.parseDouble(widthString); + + if (widthDouble <= 1.0) { // for percentage values (e.g. 0.5 = 50%). + width = (int) ((double) mapView.getWidth() * (widthDouble)); + } else { + width = (int) widthDouble; + } + } + + if (width > 0) { + layoutParams.width = width; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (styles.has("maxWidth")) { + try { + String widthString = styles.getString("maxWidth"); + + if (widthString.endsWith("%")) { + double widthDouble = Double.parseDouble(widthString.replace("%", "")); + + maxWidth = (int) ((double) mapView.getWidth() * (widthDouble / 100)); + + // make sure to take padding into account. + maxWidth -= (windowLayer.getPaddingLeft() + windowLayer.getPaddingRight()); + } else if (PluginUtil.isNumeric(widthString)) { + double widthDouble = Double.parseDouble(widthString); + + if (widthDouble <= 1.0) { // for percentage values (e.g. 0.5 = 50%). + maxWidth = (int) ((double) mapView.getWidth() * (widthDouble)); + } else { + maxWidth = (int) widthDouble; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + windowLayer.setLayoutParams(layoutParams); + + //---------------------------------------- + // text-align = left | center | right + //---------------------------------------- + int gravity = Gravity.LEFT; + int textAlignment = View.TEXT_ALIGNMENT_GRAVITY; + + if (styles != null) { + if (styles.has("text-align")) { + try { + String textAlignValue = styles.getString("text-align"); + + switch (TEXT_STYLE_ALIGNMENTS.valueOf(textAlignValue)) { + case left: + gravity = Gravity.LEFT; + textAlignment = View.TEXT_ALIGNMENT_GRAVITY; + break; + case center: + gravity = Gravity.CENTER; + textAlignment = View.TEXT_ALIGNMENT_CENTER; + break; + case right: + gravity = Gravity.RIGHT; + textAlignment = View.TEXT_ALIGNMENT_VIEW_END; + break; + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + if (title != null) { + if (title.contains("data:image/") && title.contains(";base64,")) { + tmp = title.split(","); + Bitmap image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]); + image = PluginUtil.scaleBitmapForDevice(image); + ImageView imageView = new ImageView(this.activity); + imageView.setImageBitmap(image); + + if (maxWidth > 0) { + imageView.setMaxWidth(maxWidth); + imageView.setAdjustViewBounds(true); + } + + windowLayer.addView(imageView); + } else { + TextView textView = new TextView(this.activity); + textView.setText(title); + textView.setSingleLine(false); + + int titleColor = Color.BLACK; + if (styles != null && styles.has("color")) { + try { + titleColor = PluginUtil.parsePluginColor(styles.getJSONArray("color")); + } catch (JSONException e) { + e.printStackTrace(); + } + } + textView.setTextColor(titleColor); + textView.setGravity(gravity); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + textView.setTextAlignment(textAlignment); + } + + //---------------------------------------- + // font-style = normal | italic + // font-weight = normal | bold + //---------------------------------------- + int fontStyle = Typeface.NORMAL; + if (styles != null) { + if (styles.has("font-style")) { + try { + if ("italic".equals(styles.getString("font-style"))) { + fontStyle = Typeface.ITALIC; + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + if (styles.has("font-weight")) { + try { + if ("bold".equals(styles.getString("font-weight"))) { + fontStyle = fontStyle | Typeface.BOLD; + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + } + textView.setTypeface(Typeface.DEFAULT, fontStyle); + + if (maxWidth > 0) { + textView.setMaxWidth(maxWidth); + } + + windowLayer.addView(textView); + } + } + if (snippet != null) { + //snippet = snippet.replaceAll("\n", ""); + TextView textView2 = new TextView(this.activity); + textView2.setText(snippet); + textView2.setTextColor(Color.GRAY); + textView2.setTextSize((textView2.getTextSize() / 6 * 5) / density); + textView2.setGravity(gravity); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + textView2.setTextAlignment(textAlignment); + } + + if (maxWidth > 0) { + textView2.setMaxWidth(maxWidth); + } + + windowLayer.addView(textView2); + } + + return windowLayer; + } + + @Override + public View getInfoWindow(Marker marker) { + //Log.d(TAG, "--->getInfoWindow"); + activeMarker = marker; + + JSONObject properties = null; + + String markerTag = (String) marker.getTag(); + String tmp[] = markerTag.split("_"); + String className = tmp[0]; + tmp = markerTag.split("-"); + String markerId = tmp[tmp.length - 1]; + + String propertyId = "marker_property_" + markerTag; + + //Log.e(TAG, "---> getInfoWindow / propertyId = " + propertyId); + //Log.e(TAG, "---> getInfoWindow / pluginEntryId = " + mapId + "-" + className); + PluginEntry pluginEntry = plugins.get(mapId + "-" + className); + if (pluginEntry == null) { + Log.e(TAG, "---> getInfoWindow / pluginEntry is null"); + return null; + } + MyPlugin myPlugin = (MyPlugin)pluginEntry.plugin; + + if (objects.containsKey(propertyId)) { + properties = (JSONObject) objects.get(propertyId); + try { + if (marker.getTitle() == null && marker.getSnippet() == null) { + + syncInfoWndPosition(); + + if ((marker.getTag() + "").startsWith("markercluster_")){ + this.onClusterEvent("info_open", marker); + } else { + this.onMarkerEvent("info_open", marker); + } + + int resId = PluginUtil.getAppResource(cordova.getActivity(), "dummy_infowindow", "layout"); + return cordova.getActivity().getLayoutInflater().inflate(resId, null); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { + Log.e(TAG, "---> getInfoWindow / can not find the property"); + } + return null; + } + + + /** + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setOptions(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + + final AsyncSetOptionsResult results = new AsyncSetOptionsResult(); + results.cameraPadding = DEFAULT_CAMERA_PADDING; + + try { + JSONObject params = args.getJSONObject(0); + + if (params.has("styles")) { + results.styles = params.getString("styles"); + } else { + // map type + results.MAP_TYPE_ID = -1; + if (!params.has("styles") && params.has("mapType")) { + String typeStr = params.getString("mapType"); + results.MAP_TYPE_ID = typeStr.equals("MAP_TYPE_NORMAL") ? GoogleMap.MAP_TYPE_NORMAL : results.MAP_TYPE_ID; + results.MAP_TYPE_ID = typeStr.equals("MAP_TYPE_HYBRID") ? GoogleMap.MAP_TYPE_HYBRID : results.MAP_TYPE_ID; + results.MAP_TYPE_ID = typeStr.equals("MAP_TYPE_SATELLITE") ? GoogleMap.MAP_TYPE_SATELLITE : results.MAP_TYPE_ID; + results.MAP_TYPE_ID = typeStr.equals("MAP_TYPE_TERRAIN") ? GoogleMap.MAP_TYPE_TERRAIN : results.MAP_TYPE_ID; + results.MAP_TYPE_ID = typeStr.equals("MAP_TYPE_NONE") ? GoogleMap.MAP_TYPE_NONE : results.MAP_TYPE_ID; + } + } + + + // move the camera position + if (params.has("camera")) { + LatLngBounds cameraBounds = null; + JSONObject camera = params.getJSONObject("camera"); + Builder builder = CameraPosition.builder(); + if (camera.has("bearing")) { + builder.bearing((float) camera.getDouble("bearing")); + } + if (camera.has("latLng")) { + JSONObject latLng = camera.getJSONObject("latLng"); + builder.target(new LatLng(latLng.getDouble("lat"), latLng.getDouble("lng"))); + } + + if (camera.has("padding")) { + results.cameraPadding = camera.getDouble("padding"); + } + + if (camera.has("target")) { + Object target = camera.get("target"); + @SuppressWarnings("rawtypes") + Class targetClass = target.getClass(); + if ("org.json.JSONArray".equals(targetClass.getName())) { + JSONArray points = camera.getJSONArray("target"); + cameraBounds = PluginUtil.JSONArray2LatLngBounds(points); + builder.target(cameraBounds.getCenter()); + + } else { + JSONObject latLng = camera.getJSONObject("target"); + builder.target(new LatLng(latLng.getDouble("lat"), latLng.getDouble("lng"))); + } + } + if (camera.has("tilt")) { + builder.tilt((float) camera.getDouble("tilt")); + } + if (camera.has("zoom")) { + builder.zoom((float) camera.getDouble("zoom")); + } + results.cameraPosition = builder.build(); + results.cameraBounds = cameraBounds; + + } + + + + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + + if (results.cameraPosition != null) { + try { + map.moveCamera(CameraUpdateFactory.newCameraPosition(results.cameraPosition)); + } catch (Exception e) { + e.printStackTrace(); + } + if (results.cameraBounds != null) { + fitBounds(results.cameraBounds, (int)(results.cameraPadding / density)); + } + } + + //styles + if (results.styles != null) { + MapStyleOptions styleOptions = new MapStyleOptions(results.styles); + map.setMapStyle(styleOptions); + map.setMapType(GoogleMap.MAP_TYPE_NORMAL); + } else if (results.MAP_TYPE_ID != -1) { + map.setMapType(results.MAP_TYPE_ID); + } + + JSONObject params = null; + params = args.getJSONObject(0); + UiSettings settings = map.getUiSettings(); + + //preferences + if (params.has("preferences")) { + JSONObject preferences = params.getJSONObject("preferences"); + + if (preferences.has("padding")) { + JSONObject padding = preferences.getJSONObject("padding"); + int left = 0, top = 0, bottom = 0, right = 0; + if (padding.has("left")) { + left = (int) (padding.getInt("left") * density); + } + if (padding.has("top")) { + top = (int) (padding.getInt("top") * density); + } + if (padding.has("bottom")) { + bottom = (int) (padding.getInt("bottom") * density); + } + if (padding.has("right")) { + right = (int) (padding.getInt("right") * density); + } + map.setPadding(left, top, right, bottom); + + FrameLayout.LayoutParams lParams2 = (FrameLayout.LayoutParams) dummyMyLocationButton.getLayoutParams(); + lParams2.rightMargin = right + (int)(5 * density); + lParams2.topMargin = top + (int)(5 * density); + dummyMyLocationButton.setLayoutParams(lParams2); + } + + if (preferences.has("zoom")) { + JSONObject zoom = preferences.getJSONObject("zoom"); + if (zoom.has("minZoom")) { + map.setMinZoomPreference((float)zoom.getDouble("minZoom")); + } + if (zoom.has("maxZoom")) { + map.setMaxZoomPreference((float)zoom.getDouble("maxZoom")); + } + } + + + if (preferences.has("gestureBounds")) { + Object target = preferences.get("gestureBounds"); + @SuppressWarnings("rawtypes") + Class targetClass = target.getClass(); + if ("org.json.JSONArray".equals(targetClass.getName())) { + JSONArray points = preferences.getJSONArray("gestureBounds"); + if (points.length() > 0) { + LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points); + map.setLatLngBoundsForCameraTarget(bounds); + } else { + map.setLatLngBoundsForCameraTarget(null); + } + } + } + + } + + //gestures + if (params.has("gestures")) { + JSONObject gestures = params.getJSONObject("gestures"); + + if (gestures.has("tilt")) { + settings.setTiltGesturesEnabled(gestures.getBoolean("tilt")); + } + if (gestures.has("scroll")) { + settings.setScrollGesturesEnabled(gestures.getBoolean("scroll")); + } + if (gestures.has("rotate")) { + settings.setRotateGesturesEnabled(gestures.getBoolean("rotate")); + } + if (gestures.has("zoom")) { + settings.setZoomGesturesEnabled(gestures.getBoolean("zoom")); + } + } + + //controls + if (params.has("controls")) { + final JSONObject controls = params.getJSONObject("controls"); + + if (controls.has("compass")) { + settings.setCompassEnabled(controls.getBoolean("compass")); + } + if (controls.has("zoom")) { + settings.setZoomControlsEnabled(controls.getBoolean("zoom")); + } + if (controls.has("indoorPicker")) { + settings.setIndoorLevelPickerEnabled(controls.getBoolean("indoorPicker")); + } + if (controls.has("mapToolbar")) { + settings.setMapToolbarEnabled(controls.getBoolean("mapToolbar")); + } + if (controls.has("myLocation") || controls.has("myLocationButton")) { + cordova.getThreadPool().submit(new Runnable() { + @Override + public void run() { + JSONArray args = new JSONArray(); + args.put(controls); + try { + PluginMap.this.setMyLocationEnabled(args, callbackContext); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error("error at map.setOptions()"); + } + } + }); + } else { + callbackContext.success(); + } + } else { + callbackContext.success(); + } + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error("error at map.setOptions()"); + + } + } + }); + + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error("error at map.setOptions()"); + } + + + + } + + public void getFocusedBuilding(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + IndoorBuilding focusedBuilding = map.getFocusedBuilding(); + if (focusedBuilding != null) { + JSONObject result = PluginUtil.convertIndoorBuildingToJson(focusedBuilding); + callbackContext.success(result); + } else { + callbackContext.success(-1); + } + } + }); + } + + /** + * Set center location of the marker + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setCameraTarget(JSONArray args, final CallbackContext callbackContext) throws JSONException { + double lat = args.getDouble(0); + double lng = args.getDouble(1); + + LatLng latLng = new LatLng(lat, lng); + final CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLng(latLng); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + myMoveCamera(cameraUpdate, callbackContext); + } + }); + } + + /** + * Set angle of the map view + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setCameraTilt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + float tilt = (float) args.getDouble(0); + + if (tilt > 0 && tilt <= 90) { + final float finalTilt = tilt; + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + CameraPosition currentPos = map.getCameraPosition(); + CameraPosition newPosition = new CameraPosition.Builder() + .target(currentPos.target).bearing(currentPos.bearing) + .zoom(currentPos.zoom).tilt(finalTilt).build(); + myMoveCamera(newPosition, callbackContext); + } + }); + } else { + callbackContext.error("Invalid tilt angle(" + tilt + ")"); + } + } + + public void setCameraBearing(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final float bearing = (float) args.getDouble(0); + + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + CameraPosition currentPos = map.getCameraPosition(); + CameraPosition newPosition = new CameraPosition.Builder() + .target(currentPos.target).bearing(bearing) + .zoom(currentPos.zoom).tilt(currentPos.tilt).build(); + myMoveCamera(newPosition, callbackContext); + } + }); + } + + /** + * Move the camera with animation + * @param args + * @param callbackContext + * @throws JSONException + */ + public void animateCamera(JSONArray args, CallbackContext callbackContext) throws JSONException { + this.updateCameraPosition("animateCamera", args, callbackContext); + } + + /** + * Move the camera without animation + * @param args + * @param callbackContext + * @throws JSONException + */ + public void moveCamera(JSONArray args, CallbackContext callbackContext) throws JSONException { + this.updateCameraPosition("moveCamera", args, callbackContext); + } + + + /** + * move the camera + * @param action + * @param args + * @param callbackContext + * @throws JSONException + */ + public void updateCameraPosition(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { + if (this.isRemoved) { + return; + } + final JSONObject cameraPos = args.getJSONObject(0); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + final CameraPosition.Builder builder = CameraPosition.builder(map.getCameraPosition()); + UpdateCameraAction cameraAction = new UpdateCameraAction(callbackContext, cameraPos, builder, action); + cameraAction.execute(); + + } + }); + + } + + private class UpdateCameraAction extends AsyncTask { + private Exception mException = null; + private CallbackContext mCallbackContext; + private JSONObject mCameraPos; + private CameraPosition.Builder mBuilder; + private String mAction; + + UpdateCameraAction(CallbackContext callbackContext, JSONObject cameraPos, CameraPosition.Builder builder, String action) { + super(); + this.mCallbackContext = callbackContext; + this.mCameraPos = cameraPos; + this.mBuilder = builder; + this.mAction = action; + } + + @Override + protected AsyncUpdateCameraPositionResult doInBackground(Void... voids) { + AsyncUpdateCameraPositionResult result = new AsyncUpdateCameraPositionResult(); + if (isRemoved) { + this.cancel(true); + return null; + } + + try { + + result.durationMS = 4000; + result.cameraPadding = DEFAULT_CAMERA_PADDING; + if (mCameraPos.has("tilt")) { + mBuilder.tilt((float) mCameraPos.getDouble("tilt")); + } + if (mCameraPos.has("bearing")) { + mBuilder.bearing((float) mCameraPos.getDouble("bearing")); + } + if (mCameraPos.has("zoom")) { + mBuilder.zoom((float) mCameraPos.getDouble("zoom")); + } + if (mCameraPos.has("duration")) { + result.durationMS = mCameraPos.getInt("duration"); + } + if (mCameraPos.has("padding")) { + result.cameraPadding = mCameraPos.getDouble("padding"); + } + + if (!mCameraPos.has("target")) { + return result; + } + + //------------------------ + // Create a cameraUpdate + //------------------------ + result.cameraUpdate = null; + result.cameraBounds = null; + CameraPosition newPosition; + Object target = mCameraPos.get("target"); + @SuppressWarnings("rawtypes") + Class targetClass = target.getClass(); + JSONObject latLng; + if ("org.json.JSONArray".equals(targetClass.getName())) { + JSONArray points = mCameraPos.getJSONArray("target"); + result.cameraBounds = PluginUtil.JSONArray2LatLngBounds(points); + result.cameraUpdate = CameraUpdateFactory.newLatLngBounds(result.cameraBounds, (int)(result.cameraPadding * density)); + } else { + latLng = mCameraPos.getJSONObject("target"); + mBuilder.target(new LatLng(latLng.getDouble("lat"), latLng.getDouble("lng"))); + newPosition = mBuilder.build(); + result.cameraUpdate = CameraUpdateFactory.newCameraPosition(newPosition); + } + } catch (Exception e) { + mException = e; + e.printStackTrace(); + this.cancel(true); + return null; + } + + return result; + } + + @Override + public void onCancelled() { + if (mException != null) { + mException.printStackTrace(); + } + mCallbackContext.error(mException != null ? mException.getMessage() + "" : ""); + } + @Override + public void onCancelled(AsyncUpdateCameraPositionResult AsyncUpdateCameraPositionResult) { + if (mException != null) { + mException.printStackTrace(); + } + mCallbackContext.error(mException != null ? mException.getMessage() + "" : ""); + } + + @Override + public void onPostExecute(AsyncUpdateCameraPositionResult AsyncUpdateCameraPositionResult) { + if (isRemoved) { + return; + } + + + if (AsyncUpdateCameraPositionResult.cameraUpdate == null) { + CameraPosition.Builder builder = CameraPosition.builder(map.getCameraPosition()); + builder.target(map.getCameraPosition().target); + AsyncUpdateCameraPositionResult.cameraUpdate = CameraUpdateFactory.newCameraPosition(builder.build()); + } + + final AsyncUpdateCameraPositionResult finalCameraPosition = AsyncUpdateCameraPositionResult; + PluginUtil.MyCallbackContext myCallback = new PluginUtil.MyCallbackContext("moveCamera", webView) { + @Override + public void onResult(final PluginResult pluginResult) { + if (finalCameraPosition.cameraBounds != null && ANIMATE_CAMERA_DONE.equals(pluginResult.getStrMessage())) { + + + final Builder builder = CameraPosition.builder(map.getCameraPosition()); + if (mCameraPos.has("tilt")) { + try { + builder.tilt((float) mCameraPos.getDouble("tilt")); + } catch (JSONException e) { + e.printStackTrace(); + } + } + if (mCameraPos.has("bearing")) { + try { + builder.bearing((float) mCameraPos.getDouble("bearing")); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(finalCameraPosition.cameraBounds, (int)(finalCameraPosition.cameraPadding * density)); + try { + map.moveCamera(cameraUpdate); + } catch (Exception e) { + e.printStackTrace(); + } + map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { + @Override + public void onCameraIdle() { + PluginMap.this.onCameraIdle(); + map.setOnCameraIdleListener(PluginMap.this); + builder.zoom(map.getCameraPosition().zoom); + builder.target(map.getCameraPosition().target); + map.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build())); + } + }); + } else { + final Builder builder = CameraPosition.builder(map.getCameraPosition()); + if (mCameraPos.has("tilt")) { + try { + builder.tilt((float) mCameraPos.getDouble("tilt")); + } catch (JSONException e) { + e.printStackTrace(); + } + } + if (mCameraPos.has("bearing")) { + try { + builder.bearing((float) mCameraPos.getDouble("bearing")); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + try { + map.moveCamera(finalCameraPosition.cameraUpdate); + } catch (Exception e) { + e.printStackTrace(); + } + + builder.zoom(map.getCameraPosition().zoom); + builder.target(map.getCameraPosition().target); + + map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { + @Override + public void onCameraIdle() { + PluginMap.this.onCameraIdle(); + map.setOnCameraIdleListener(PluginMap.this); + builder.zoom(map.getCameraPosition().zoom); + builder.target(map.getCameraPosition().target); + map.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build())); + } + }); + } + mCallbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); + } + }; + if (mAction.equals("moveCamera")) { + myMoveCamera(AsyncUpdateCameraPositionResult.cameraUpdate, myCallback); + } else { + myAnimateCamera(mapId, AsyncUpdateCameraPositionResult.cameraUpdate, AsyncUpdateCameraPositionResult.durationMS, myCallback); + } + + } + } + + /** + * Set zoom of the map + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setCameraZoom(JSONArray args, final CallbackContext callbackContext) throws JSONException { + final Long zoom = args.getLong(0); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + myMoveCamera(CameraUpdateFactory.zoomTo(zoom), callbackContext); + } + }); + } + + + /** + * Stop camera animation + * @param args + * @param callbackContext + * @throws JSONException + */ + public void stopAnimation(JSONArray args, final CallbackContext callbackContext) throws JSONException { + + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if(map != null) { + map.stopAnimation(); + } + callbackContext.success(); + } + }); + } + + /** + * Pan by the specified pixel + * @param args + * @param callbackContext + * @throws JSONException + */ + public void panBy(JSONArray args, final CallbackContext callbackContext) throws JSONException { + int x = args.getInt(0); + int y = args.getInt(1); + float xPixel = -x * density; + float yPixel = -y * density; + final CameraUpdate cameraUpdate = CameraUpdateFactory.scrollBy(xPixel, yPixel); + + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + map.animateCamera(cameraUpdate); + callbackContext.success(); + } + }); + } + + /** + * Move the camera of the map + * @param cameraPosition + * @param callbackContext + */ + public void myMoveCamera(CameraPosition cameraPosition, final CallbackContext callbackContext) { + final CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + myMoveCamera(cameraUpdate, callbackContext); + } + }); + } + + /** + * Move the camera of the map + * @param cameraUpdate + * @param callbackContext + */ + public void myMoveCamera(CameraUpdate cameraUpdate, CallbackContext callbackContext) { + try { + map.moveCamera(cameraUpdate); + } catch (Exception e) { + e.printStackTrace(); + } + callbackContext.success(); + } + + + /** + * Enable MyLocation feature if set true + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setMyLocationEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + final JSONObject params = args.getJSONObject(0); + + boolean locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED; + //Log.d(TAG, "---> setMyLocationEnabled, hasPermission = " + locationPermission); + + if (!locationPermission) { + //_saveArgs = args; + //_saveCallbackContext = callbackContext; + synchronized (semaphore) { + cordova.requestPermissions(this, callbackContext.hashCode(), new String[]{ + Manifest.permission.ACCESS_FINE_LOCATION + }); + try { + semaphore.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_GRANTED; + + //Log.d(TAG, "---> (1720)setMyLocationEnabled, hasPermission = " + locationPermission); + + if (!locationPermission) { + callbackContext.error(PluginUtil.getPgmStrings(activity,"pgm_location_rejected_by_user")); + return; + } + + } + + this.activity.runOnUiThread(new Runnable() { + @SuppressLint("MissingPermission") + @Override + public void run() { + try { + + Boolean isMyLocationEnabled = false; + if (params.has("myLocation")) { + //Log.d(TAG, "--->myLocation = " + params.getBoolean("myLocation")); + isMyLocationEnabled = params.getBoolean("myLocation"); + map.setMyLocationEnabled(isMyLocationEnabled); + } + + Boolean isMyLocationButtonEnabled = false; + if (params.has("myLocationButton")) { + //Log.d(TAG, "--->myLocationButton = " + params.getBoolean("myLocationButton")); + isMyLocationButtonEnabled = params.getBoolean("myLocationButton"); + map.getUiSettings().setMyLocationButtonEnabled(isMyLocationButtonEnabled); + } + //Log.d(TAG, "--->isMyLocationButtonEnabled = " + isMyLocationButtonEnabled + ", isMyLocationEnabled = " + isMyLocationEnabled); + if (!isMyLocationEnabled && isMyLocationButtonEnabled) { + dummyMyLocationButton.setVisibility(View.VISIBLE); + } else { + dummyMyLocationButton.setVisibility(View.GONE); + } + + } catch (Exception e) { + e.printStackTrace(); + } + callbackContext.success(); + } + }); + } + + /** + * Clear all markups + * @param args Parameters given from JavaScript side + * @param callbackContext Callback contect for sending back the result. + * @throws JSONException + */ + @SuppressWarnings("unused") + public void clear(JSONArray args, final CallbackContext callbackContext) throws JSONException { + + Set pluginNames = plugins.keySet(); + Iterator iterator = pluginNames.iterator(); + String pluginName; + PluginEntry pluginEntry; + while(iterator.hasNext()) { + pluginName = iterator.next(); + if (!"Map".equals(pluginName)) { + pluginEntry = plugins.get(pluginName); + ((MyPlugin) pluginEntry.plugin).clear(); + } + } + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + boolean isSuccess = false; + while (!isSuccess) { + try { + map.clear(); + isSuccess = true; + } catch (Exception e) { + e.printStackTrace(); + isSuccess = false; + } + } + if (callbackContext != null) { + callbackContext.success(); + } + } + }); + + } + + /** + * Enable Indoor map feature if set true + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setIndoorEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final Boolean isEnabled = args.getBoolean(0); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + map.setIndoorEnabled(isEnabled); + callbackContext.success(); + } + }); + } + + /** + * Enable the traffic layer if set true + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setTrafficEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final Boolean isEnabled = args.getBoolean(0); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + map.setTrafficEnabled(isEnabled); + callbackContext.success(); + } + }); + } + + /** + * Enable the compass if set true + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setCompassEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final Boolean isEnabled = args.getBoolean(0); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + UiSettings uiSettings = map.getUiSettings(); + uiSettings.setCompassEnabled(isEnabled); + callbackContext.success(); + } + }); + } + + /** + * Change the map type id of the map + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setMapTypeId(JSONArray args, final CallbackContext callbackContext) throws JSONException { + + int mapTypeId = -1; + String typeStr = args.getString(0); + mapTypeId = typeStr.equals("MAP_TYPE_NORMAL") ? GoogleMap.MAP_TYPE_NORMAL : mapTypeId; + mapTypeId = typeStr.equals("MAP_TYPE_HYBRID") ? GoogleMap.MAP_TYPE_HYBRID : mapTypeId; + mapTypeId = typeStr.equals("MAP_TYPE_SATELLITE") ? GoogleMap.MAP_TYPE_SATELLITE : mapTypeId; + mapTypeId = typeStr.equals("MAP_TYPE_TERRAIN") ? GoogleMap.MAP_TYPE_TERRAIN : mapTypeId; + mapTypeId = typeStr.equals("MAP_TYPE_NONE") ? GoogleMap.MAP_TYPE_NONE : mapTypeId; + + if (mapTypeId == -1) { + callbackContext.error("Unknown MapTypeID is specified:" + typeStr); + return; + } + + final int myMapTypeId = mapTypeId; + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + map.setMapType(myMapTypeId); + callbackContext.success(); + } + }); + } + + + /** + * Move the camera of the map + * @param cameraUpdate + * @param durationMS + * @param callbackContext + */ + public void myAnimateCamera(final String mapId, final CameraUpdate cameraUpdate, final int durationMS, final CallbackContext callbackContext) { + final GoogleMap.CancelableCallback callback = new GoogleMap.CancelableCallback() { + @Override + public void onFinish() { + callbackContext.success(ANIMATE_CAMERA_DONE); + } + + @Override + public void onCancel() { + callbackContext.success(ANIMATE_CAMERA_CANCELED); + } + }; + + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (durationMS > 0) { + map.animateCamera(cameraUpdate, durationMS, callback); + } else { + map.animateCamera(cameraUpdate, callback); + } + } + }); + } + + + /** + * Return the current position of the camera + * @param args + * @param callbackContext + * @throws JSONException + */ + public void getCameraPosition(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + CameraPosition camera = map.getCameraPosition(); + JSONObject json = new JSONObject(); + JSONObject latlng = new JSONObject(); + try { + latlng.put("lat", camera.target.latitude); + latlng.put("lng", camera.target.longitude); + json.put("target", latlng); + json.put("zoom", camera.zoom); + json.put("tilt", camera.tilt); + json.put("bearing", camera.bearing); + json.put("hashCode", camera.hashCode()); + + callbackContext.success(json); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error(e.getMessage() + ""); + } + } + }); + } + + /** + * Return the image data encoded with base64 + * @param args + * @param callbackContext + * @throws JSONException + */ + public void toDataURL(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + JSONObject params = args.getJSONObject(0); + boolean uncompress = false; + if (params.has("uncompress")) { + uncompress = params.getBoolean("uncompress"); + } + final boolean finalUncompress = uncompress; + + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + map.snapshot(new GoogleMap.SnapshotReadyCallback() { + + @Override + public void onSnapshotReady(final Bitmap image) { + AsyncTask.execute(new Runnable() { + @Override + public void run() { + Bitmap image2 = image; + if (!finalUncompress) { + image2 = PluginUtil.resizeBitmap(image, + (int) (image2.getWidth() * density), + (int) (image2.getHeight() * density)); + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + image2.compress(Bitmap.CompressFormat.PNG, 100, outputStream); + byte[] byteArray = outputStream.toByteArray(); + + callbackContext.success("data:image/png;base64," + + Base64.encodeToString(byteArray, Base64.NO_WRAP)); + } + }); + } + }); + } + }); + + } + public void fromLatLngToPoint(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + double lat, lng; + lat = args.getDouble(0); + lng = args.getDouble(1); + final LatLng latLng = new LatLng(lat, lng); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + projection = map.getProjection(); + Point point = projection.toScreenLocation(latLng); + try { + JSONArray pointJSON = new JSONArray(); + pointJSON.put((int)((double)point.x / (double)density)); + pointJSON.put((int)((double)point.y / (double)density)); + callbackContext.success(pointJSON); + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error(e.getMessage() + ""); + } + } + }); + } + + public void fromPointToLatLng(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + double pointX, pointY; + pointX = args.getDouble(0); + pointY = args.getDouble(1); + final Point point = new Point(); + point.x = (int)(pointX * density); + point.y = (int)(pointY * density); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + projection = map.getProjection(); + LatLng latlng = projection.fromScreenLocation(point); + try { + JSONArray pointJSON = new JSONArray(); + pointJSON.put(latlng.latitude); + pointJSON.put(latlng.longitude); + callbackContext.success(pointJSON); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error(e.getMessage() + ""); + } + } + }); + } + + + + /** + * Sets the preference for whether all gestures should be enabled or disabled. + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setAllGesturesEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final Boolean isEnabled = args.getBoolean(0); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + UiSettings uiSettings = map.getUiSettings(); + uiSettings.setAllGesturesEnabled(isEnabled); + callbackContext.success(); + } + }); + } + + /** + * Sets padding of the map + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setPadding(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + JSONObject padding = args.getJSONObject(0); + final int left = (int)(padding.getInt("left") * density); + final int top = (int)(padding.getInt("top") * density); + final int bottom = (int)(padding.getInt("bottom") * density); + final int right = (int)(padding.getInt("right") * density); + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + map.setPadding(left, top, right, bottom); + + FrameLayout.LayoutParams lParams2 = (FrameLayout.LayoutParams) dummyMyLocationButton.getLayoutParams(); + lParams2.rightMargin = right + (int)(5 * density); + lParams2.topMargin = top + (int)(5 * density); + dummyMyLocationButton.setLayoutParams(lParams2); + + callbackContext.success(); + } + }); + } + + /** + * update the active marker (for internal use) + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setActiveMarkerId(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String id = args.getString(0); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Marker marker = (Marker) objects.get(id); + if (marker != null) { + activeMarker = marker; + } + callbackContext.success(); + } + }); + } + + + @Override + public boolean onMarkerClick(Marker marker) { + //Log.d(TAG, "---> onMarkerClick / marker.tag = " + marker.getTag()); + + JSONObject properties = null; + String clusterId_markerId = marker.getTag() + ""; + if (clusterId_markerId.contains("markercluster_")) { + if (clusterId_markerId.contains("-marker_")) { + activeMarker = marker; + this.onClusterEvent("marker_click", activeMarker); + } else { + if (activeMarker != null) { + this.onMarkerEvent("info_close", activeMarker); + } + } + this.onClusterEvent("cluster_click", marker); + } else { + webView.loadUrl("javascript:if(window.cordova){cordova.fireDocumentEvent('plugin_touch', {});}"); + this.onMarkerEvent("marker_click", marker); + activeMarker = marker; + } + + String tmp[] = clusterId_markerId.split("_"); + String className = tmp[0]; + + PluginEntry pluginEntry = plugins.get(mapId + "-" + className); + if (pluginEntry == null) { + return true; + } + MyPlugin myPlugin = (MyPlugin)pluginEntry.plugin; + String propertyId = "marker_property_" + clusterId_markerId; + //Log.d(TAG, "---> onMarkerClick / propertyId = " + propertyId); + if (objects.containsKey(propertyId)) { + properties = (JSONObject) objects.get(propertyId); + if (properties.has("disableAutoPan")) { + boolean disableAutoPan = false; + try { + disableAutoPan = properties.getBoolean("disableAutoPan"); + } catch (JSONException e) { + e.printStackTrace(); + } + if (disableAutoPan) { + marker.showInfoWindow(); + return true; + } else { + marker.showInfoWindow(); + return false; + } + } + } + + marker.showInfoWindow(); + return true; + //return false; + } + + @Override + public void onInfoWindowClick(Marker marker) { + activeMarker = marker; + syncInfoWndPosition(); + String markerTag = marker.getTag() + ""; + if (markerTag.startsWith("markercluster_")){ + this.onClusterEvent("info_click", marker); + } else { + this.onMarkerEvent("info_click", marker); + } + } + + @Override + public void onMarkerDrag(Marker marker) { + if (marker.equals(activeMarker)) { + syncInfoWndPosition(); + } + if ((marker.getTag() + "").startsWith("markercluster_")){ + this.onClusterEvent("marker_drag", marker); + } else { + this.onMarkerEvent("marker_drag", marker); + } + } + + @Override + public void onMarkerDragEnd(Marker marker) { + if (marker.equals(activeMarker)) { + syncInfoWndPosition(); + } + if ((marker.getTag() + "").startsWith("markercluster_")){ + this.onClusterEvent("marker_drag_end", marker); + } else { + this.onMarkerEvent("marker_drag_end", marker); + } + } + + @Override + public void onMarkerDragStart(Marker marker) { + if (marker.equals(activeMarker)) { + syncInfoWndPosition(); + } + if ((marker.getTag() + "").startsWith("markercluster_")){ + this.onClusterEvent("marker_drag_start", marker); + } else { + this.onMarkerEvent("marker_drag_start", marker); + } + } + + @Override + public void onInfoWindowLongClick(Marker marker) { + activeMarker = marker; + syncInfoWndPosition(); + if ((marker.getTag() + "").startsWith("markercluster_")){ + this.onClusterEvent("info_long_click", marker); + } else { + this.onMarkerEvent("info_long_click", marker); + } + } + + @Override + public void onInfoWindowClose(Marker marker) { + //Log.d(TAG, "--->onInfoWindowClose"); + boolean useHtmlInfoWnd = marker.getTitle() == null && + marker.getSnippet() == null; + if (useHtmlInfoWnd) { + String markerTag = marker.getTag() + ""; + if (markerTag.startsWith("markercluster_")){ + if (markerTag.contains("-marker_")) { + this.onClusterEvent("info_close", marker); + } + } else { + this.onMarkerEvent("info_close", marker); + } + } else { + this.onMarkerEvent("info_close", marker); + } + //activeMarker = null; // <-- This causes HTMLinfoWindow is not able to close when you tap on the map. + } + + @Override + public void onMapLoaded() { + this.onCameraEvent("camera_move_end"); + } + + + + /******************************************************** + * Callbacks + ********************************************************/ + + /** + * Notify marker event to JS + * @param eventName + * @param marker + */ + public void onMarkerEvent(String eventName, Marker marker) { + if (marker.getTag() == null) { + return; + } + LatLng latLng = marker.getPosition(); + + String markerTag = (String) marker.getTag(); + String tmp[] = markerTag.split("_"); + tmp = markerTag.split("-"); + String markerId = tmp[tmp.length - 1]; + String js = String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: '%s', callback:'_onMarkerEvent', args:['%s', new plugin.google.maps.LatLng(%f, %f)]});}", + mapId, mapId, eventName, markerId, latLng.latitude, latLng.longitude); + jsCallback(js); + } + public void onClusterEvent(String eventName, Marker marker) { + if (marker.getTag() == null) { + return; + } + LatLng latLng = marker.getPosition(); + + String markerTag = (String) marker.getTag(); + String tmp[] = markerTag.split("-"); + String clusterId = tmp[0]; + String markerId = tmp[1]; + String js = String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: '%s', callback:'_onClusterEvent', args:['%s', '%s', new plugin.google.maps.LatLng(%f, %f)]});}", + mapId, mapId, eventName, clusterId, markerId, latLng.latitude, latLng.longitude); + jsCallback(js); + } + public void syncInfoWndPosition() { + if (activeMarker == null) { + Log.d(TAG, "--->no active marker"); + return; + } + LatLng latLng = activeMarker.getPosition(); + Point point = projection.toScreenLocation(latLng); + + String js = String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: 'syncPosition', callback:'_onSyncInfoWndPosition', args:[{'x': %d, 'y': %d}]});}", + mapId, mapId, (int)(point.x / density), (int)(point.y / density)); + jsCallback(js); + } + + public void onOverlayEvent(String eventName, String overlayId, LatLng point) { + String js = String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: '%s', callback:'_onOverlayEvent', args:['%s', new plugin.google.maps.LatLng(%f, %f)]});}", + mapId, mapId, eventName, overlayId, point.latitude, point.longitude); + jsCallback(js); + } + public void onPolylineClick(Polyline polyline, LatLng point) { + String overlayId = "polyline_" + polyline.getTag(); + this.onOverlayEvent("polyline_click", overlayId, point); + } + public void onPolygonClick(Polygon polygon, LatLng point) { + String overlayId = "polygon_" + polygon.getTag(); + this.onOverlayEvent("polygon_click", overlayId, point); + } + public void onCircleClick(Circle circle, LatLng point) { + String overlayId = "circle_" + circle.getTag(); + this.onOverlayEvent("circle_click", overlayId, point); + } + public void onGroundOverlayClick(GroundOverlay groundOverlay, LatLng point) { + String overlayId = "groundoverlay_" + groundOverlay.getTag(); + this.onOverlayEvent("groundoverlay_click", overlayId, point); + } + + /** + * Notify map event to JS + * @param eventName + */ + public void onMapEvent(final String eventName) { + String js = String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: '%s', callback:'_onMapEvent', args:[]});}", + mapId, mapId, eventName); + jsCallback(js); + } + + /** + * Notify map event to JS + * @param eventName + * @param point + */ + public void onMapEvent(final String eventName, final LatLng point) { + String js = String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: '%s', callback:'_onMapEvent', args:[new plugin.google.maps.LatLng(%f, %f)]});}", + mapId, mapId, eventName, point.latitude, point.longitude); + jsCallback(js); + } + + @Override + public void onMapLongClick(LatLng point) { + this.onMapEvent("map_long_click", point); + } + + private double calculateDistance(LatLng pt1, LatLng pt2){ + float[] results = new float[1]; + Location.distanceBetween(pt1.latitude, pt1.longitude, + pt2.latitude, pt2.longitude, results); + return results[0]; + } + + /** + * Intersection for non-geodesic line + * @ref http://movingahead.seesaa.net/article/299962216.html + * @ref http://www.softsurfer.com/Archive/algorithm_0104/algorithm_0104B.htm#Line-Plane + * + * @param points + * @param point + * @return LatLng on the line + */ + private LatLng isPointOnTheLine(List points, LatLng point) { + double Sx, Sy; + Point p0, p1, touchPoint; + touchPoint = projection.toScreenLocation(point); + + p0 = projection.toScreenLocation(points.get(0)); + for (int i = 1; i < points.size(); i++) { + p1 = projection.toScreenLocation(points.get(i)); + Sx = ((double)touchPoint.x - (double)p0.x) / ((double)p1.x - (double)p0.x); + Sy = ((double)touchPoint.y - (double)p0.y) / ((double)p1.y - (double)p0.y); + if (Math.abs(Sx - Sy) < 0.05 && Sx < 1 && Sx > 0) { + return points.get(i); + } + p0 = p1; + } + return null; + } + + /** + * Intersection for geodesic line + * @ref http://my-clip-devdiary.blogspot.com/2014/01/html5canvas.html + * + * @param points + * @param point + * @param threshold + * @return LatLng on the line + */ + private LatLng isPointOnTheGeodesicLine(List points, final LatLng point, double threshold) { + + double trueDistance, testDistance1, testDistance2; + Point p0, p1; + int fingerSize = (int)(20 * density); // assume finger size is 20px + + // clicked point(latlng) -> pixels + Point touchPoint = projection.toScreenLocation(point); + LatLngBounds possibleBounds = new LatLngBounds(point, point); + Point nePoint = new Point(touchPoint.x - fingerSize, touchPoint.y - fingerSize); + Point swPoint = new Point(touchPoint.x + fingerSize, touchPoint.y + fingerSize); + + possibleBounds = possibleBounds.including(projection.fromScreenLocation(nePoint)); + possibleBounds = possibleBounds.including(projection.fromScreenLocation(swPoint)); + + //-------------------------- + // debug: draw rectangle + //-------------------------- +// PolylineOptions polylineOptions = new PolylineOptions(); +// polylineOptions.add(possibleBounds.northeast); +// polylineOptions.add(new LatLng(possibleBounds.northeast.latitude, possibleBounds.southwest.longitude)); +// polylineOptions.add(possibleBounds.southwest); +// polylineOptions.add(new LatLng(possibleBounds.southwest.latitude, possibleBounds.northeast.longitude)); +// polylineOptions.add(possibleBounds.northeast); +// map.addPolyline(polylineOptions); + + //---------------------------------------------------------------- + // Detect the clicked-point is closer to the line or not + //---------------------------------------------------------------- + LatLng start = null, finish = null; + for (int i = 0; i < points.size() - 1; i++) { + p0 = new Point(); + p0.x = (int) (points.get(i).latitude * 100000); + p0.y = (int) (points.get(i).longitude * 100000); + p1 = new Point(); + p1.x = (int) (points.get(i + 1).latitude * 100000); + p1.y = (int) (points.get(i + 1).longitude * 100000); + trueDistance = this.calculateDistance(points.get(i), points.get(i + 1)); + testDistance1 = this.calculateDistance(points.get(i), point); + testDistance2 = this.calculateDistance(point, points.get(i + 1)); + // the distance is exactly same if the point is on the straight line + if (Math.abs(trueDistance - (testDistance1 + testDistance2)) < threshold) { + if (i == 0) { + start = points.get(0); + finish = points.get(1); + } else if (i == points.size() - 1) { + start = points.get(i - 1); + finish = points.get(i); + } else { + start = points.get(i); + finish = points.get(i + 1); + } + break; + } + } + + if (start == null) { + return null; + } + + if (start.longitude > finish.longitude) { + LatLng tmp = start; + start = finish; + finish = tmp; + } + + //-------------------------- + // debug: draw rectangle + //-------------------------- +// LatLngBounds _targetBounds = new LatLngBounds(start, finish); +// PolylineOptions polylineOptions2 = new PolylineOptions(); +// polylineOptions2.add(_targetBounds.northeast); +// polylineOptions2.add(new LatLng(_targetBounds.northeast.latitude, _targetBounds.southwest.longitude)); +// polylineOptions2.add(_targetBounds.southwest); +// polylineOptions2.add(new LatLng(_targetBounds.southwest.latitude, _targetBounds.northeast.longitude)); +// polylineOptions2.add(_targetBounds.northeast); +// map.addPolyline(polylineOptions2); + + + //---------------------------------------------------------------- + // Calculate waypoints from start to finish on geodesic line + // @ref http://jamesmccaffrey.wordpress.com/2011/04/17/drawing-a-geodesic-line-for-bing-maps-ajax/ + //---------------------------------------------------------------- + + // convert to radians + double lat1 = start.latitude * (Math.PI / 180.0); + double lng1 = start.longitude * (Math.PI / 180.0); + double lat2 = finish.latitude * (Math.PI / 180.0); + double lng2 = finish.longitude * (Math.PI / 180.0); + + double d = 2 * Math.asin(Math.sqrt(Math.pow((Math.sin((lat1 - lat2) / 2)), 2) + + Math.cos(lat1) * Math.cos(lat2) * Math.pow((Math.sin((lng1 - lng2) / 2)), 2))); + List wayPoints = new ArrayList(); + double f = 0.00000000f; // fraction of the curve + double finc = 0.01000000f; // fraction increment + + while (f <= 1.00000000f) { + double A = Math.sin((1.0 - f) * d) / Math.sin(d); + double B = Math.sin(f * d) / Math.sin(d); + + double x = A * Math.cos(lat1) * Math.cos(lng1) + B * Math.cos(lat2) * Math.cos(lng2); + double y = A * Math.cos(lat1) * Math.sin(lng1) + B * Math.cos(lat2) * Math.sin(lng2); + double z = A * Math.sin(lat1) + B * Math.sin(lat2); + double lat = Math.atan2(z, Math.sqrt((x*x) + (y*y))); + double lng = Math.atan2(y, x); + + LatLng wp = new LatLng(lat / (Math.PI / 180.0), lng / ( Math.PI / 180.0)); + if (possibleBounds.contains(wp)) { + wayPoints.add(wp); + //map.addMarker(new MarkerOptions().position(wp)); + } + + f += finc; + } // while + + // break into waypoints with negative longitudes and those with positive longitudes + List negLons = new ArrayList(); // lat-lons where the lon part is negative + List posLons = new ArrayList(); + List connect = new ArrayList(); + + for (int i = 0; i < wayPoints.size(); ++i) { + if (wayPoints.get(i).longitude <= 0.0f) + negLons.add(wayPoints.get(i)); + else + posLons.add(wayPoints.get(i)); + } + + // we may have to connect over 0.0 longitude + for (int i = 0; i < wayPoints.size() - 1; ++i) { + if (wayPoints.get(i).longitude <= 0.0f && wayPoints.get(i+1).longitude >= 0.0f || + wayPoints.get(i).longitude >= 0.0f && wayPoints.get(i+1).longitude <= 0.0f) { + if (Math.abs(wayPoints.get(i).longitude) + Math.abs(wayPoints.get(i+1).longitude) < 100.0f) { + connect.add(wayPoints.get(i)); + connect.add(wayPoints.get(i+1)); + } + } + } + + ArrayList inspectPoints = new ArrayList(); + if (negLons.size() >= 2) { + inspectPoints.addAll(negLons); + } + if (posLons.size() >= 2) { + inspectPoints.addAll(posLons); + } + if (connect.size() >= 2) { + inspectPoints.addAll(connect); + } + + if (inspectPoints.size() == 0) { + return null; + } + + + double minDistance = 999999999; + double distance; + LatLng mostClosePoint = null; + + for (int i = 0; i < inspectPoints.size(); i++) { + distance = this.calculateDistance(point, inspectPoints.get(i)); + if (distance < minDistance) { + minDistance = distance; + mostClosePoint = inspectPoints.get(i); + } + } + return mostClosePoint; + } + + /** + * Intersects using the Winding Number Algorithm + * @ref http://www.nttpc.co.jp/company/r_and_d/technology/number_algorithm.html + * @param path + * @param point + * @return + */ + private boolean isPolygonContains(List path, LatLng point) { + int wn = 0; + VisibleRegion visibleRegion = projection.getVisibleRegion(); + LatLngBounds bounds = visibleRegion.latLngBounds; + Point sw = projection.toScreenLocation(bounds.southwest); + + Point touchPoint = projection.toScreenLocation(point); + touchPoint.y = sw.y - touchPoint.y; + double vt; + + for (int i = 0; i < path.size() - 1; i++) { + Point a = projection.toScreenLocation(path.get(i)); + a.y = sw.y - a.y; + Point b = projection.toScreenLocation(path.get(i + 1)); + b.y = sw.y - b.y; + + if ((a.y <= touchPoint.y) && (b.y > touchPoint.y)) { + vt = ((double)touchPoint.y - (double)a.y) / ((double)b.y - (double)a.y); + if (touchPoint.x < ((double)a.x + (vt * ((double)b.x - (double)a.x)))) { + wn++; + } + } else if ((a.y > touchPoint.y) && (b.y <= touchPoint.y)) { + vt = ((double)touchPoint.y - (double)a.y) / ((double)b.y - (double)a.y); + if (touchPoint.x < ((double)a.x + (vt * ((double)b.x - (double)a.x)))) { + wn--; + } + } + } + + return (wn != 0); + } + + /** + * Check if a circle contains a point + * @param circle Instance of Circle class + * @param point LatLng + */ + private boolean isCircleContains(Circle circle, LatLng point) { + double r = circle.getRadius(); + LatLng center = circle.getCenter(); + double cX = center.latitude; + double cY = center.longitude; + double pX = point.latitude; + double pY = point.longitude; + + float[] results = new float[1]; + + Location.distanceBetween(cX, cY, pX, pY, results); + + if(results[0] < r) { + return true; + } else { + return false; + } + } + + /** + * Check if a ground overlay contains a point + * @param groundOverlay + * @param point + */ + private boolean isGroundOverlayContains(GroundOverlay groundOverlay, LatLng point) { + LatLngBounds groundOverlayBounds = groundOverlay.getBounds(); + + return groundOverlayBounds.contains(point); + } + + @Override + public boolean onMyLocationButtonClick() { + jsCallback(String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: 'my_location_button_click', callback:'_onMapEvent'});}",mapId, mapId)); + return false; + } + + @Override + public void onMyLocationClick(@NonNull Location location) { + PluginLocationService.setLastLocation(location); + try { + JSONObject result = PluginUtil.location2Json(location); + jsCallback(String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: 'my_location_click', callback:'_onMapEvent', args: [%s]});}", mapId, mapId, result.toString(0))); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + /** + * Notify the myLocationChange event to JS + */ + private void onCameraEvent(final String eventName) { + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + CameraPosition position = map.getCameraPosition(); + JSONObject params = new JSONObject(); + String jsonStr = ""; + try { + params.put("bearing", position.bearing); + params.put("tilt", position.tilt); + params.put("zoom", position.zoom); + + JSONObject target = new JSONObject(); + target.put("lat", position.target.latitude); + target.put("lng", position.target.longitude); + params.put("target", target); + + VisibleRegion visibleRegion = projection.getVisibleRegion(); + LatLngBounds latLngBounds = visibleRegion.latLngBounds; + + JSONObject northeast = new JSONObject(); + northeast.put("lat", latLngBounds.northeast.latitude); + northeast.put("lng", latLngBounds.northeast.longitude); + params.put("northeast", northeast); + + JSONObject southwest = new JSONObject(); + southwest.put("lat", latLngBounds.southwest.latitude); + southwest.put("lng", latLngBounds.southwest.longitude); + params.put("southwest", southwest); + + JSONObject nearLeft = new JSONObject(); + nearLeft.put("lat", visibleRegion.nearLeft.latitude); + nearLeft.put("lng", visibleRegion.nearLeft.longitude); + params.put("nearLeft", nearLeft); + + JSONObject nearRight = new JSONObject(); + nearRight.put("lat", visibleRegion.nearRight.latitude); + nearRight.put("lng", visibleRegion.nearRight.longitude); + params.put("nearRight", nearRight); + + JSONObject farLeft = new JSONObject(); + farLeft.put("lat", visibleRegion.farLeft.latitude); + farLeft.put("lng", visibleRegion.farLeft.longitude); + params.put("farLeft", farLeft); + + JSONObject farRight = new JSONObject(); + farRight.put("lat", visibleRegion.farRight.latitude); + farRight.put("lng", visibleRegion.farRight.longitude); + params.put("farRight", farRight); + + jsonStr = params.toString(); + } catch (JSONException e) { + e.printStackTrace(); + } + + jsCallback( + String.format( + Locale.ENGLISH, + "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'%s', callback:'_onCameraEvent', args: [%s]});}", + mapId, mapId, eventName, jsonStr)); + } + }); + + if (activeMarker != null) { + syncInfoWndPosition(); + } + } + + @Override + public void onCameraIdle() { + projection = map.getProjection(); + if (this.isDragging) { + onMapEvent("map_drag_end"); + } + this.isDragging = false; + onCameraEvent("camera_move_end"); + } + + @Override + public void onCameraMoveCanceled() { + projection = map.getProjection(); + if (this.isDragging) { + onMapEvent("map_drag_end"); + } + this.isDragging = false; + onCameraEvent("camera_move_end"); + } + + @Override + public void onCameraMove() { + projection = map.getProjection(); + if (this.isDragging) { + onMapEvent("map_drag"); + } + onCameraEvent("camera_move"); + } + + @Override + public void onCameraMoveStarted(final int reason) { + projection = map.getProjection(); + + // In order to pass the gesture parameter to the callbacks, + // use the _onMapEvent callback instead of the _onCameraEvent callback. + this.isDragging = reason == REASON_GESTURE; + + if (this.isDragging) { + onMapEvent("map_drag_start"); + } + onCameraEvent("camera_move_start"); + + + } + + @Override + public void onIndoorBuildingFocused() { + IndoorBuilding building = map.getFocusedBuilding(); + String jsonStr = "undefined"; + if (building != null) { + JSONObject result = PluginUtil.convertIndoorBuildingToJson(building); + if (result != null) { + jsonStr = result.toString(); + } + } + jsCallback(String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'indoor_building_focused', callback:'_onMapEvent', args: [%s]});}", mapId, mapId, jsonStr)); + } + + @Override + public void onIndoorLevelActivated(IndoorBuilding building) { + String jsonStr = "null"; + if (building != null) { + JSONObject result = PluginUtil.convertIndoorBuildingToJson(building); + if (result != null) { + jsonStr = result.toString(); + } + } + jsCallback(String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'indoor_level_activated', callback:'_onMapEvent', args: [%s]});}", mapId, mapId, jsonStr)); + } + @Override + public void onPoiClick(PointOfInterest pointOfInterest) { + String js = String.format(Locale.ENGLISH, "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName: '%s', callback:'_onMapEvent', args:['%s', \"%s\", new plugin.google.maps.LatLng(%f, %f)]});}", + mapId, mapId, "poi_click", pointOfInterest.placeId, pointOfInterest.name, pointOfInterest.latLng.latitude, pointOfInterest.latLng.longitude); + jsCallback(js); + } + + private void jsCallback(final String js) { + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + webView.loadUrl(js); + } + }); + } + + /** + * Notify map click event to JS, also checks for click on a polygon and triggers onPolygonEvent + * @param point + */ + public void onMapClick(final LatLng point) { + + cordova.getThreadPool().execute(new Runnable() { + @Override + public void run() { + + //---------------------------------------------------------------------- + // Pick up overlays that contains the touchpoint in the hit area bounds + //---------------------------------------------------------------------- + LatLngBounds bounds; + final HashMap boundsHitList = new HashMap(); + + PluginEntry pluginEntry; + MyPlugin myPlugin; + String[] keys; + JSONObject properties; + String pluginName, key; + //String pluginNames[] = plugins.keySet().toArray(new String[plugins.size()]); + int i, j; + try { + //for (i = 0; i < pluginNames.length; i++) { + //pluginName = pluginNames[i]; + + //if (pluginName.contains("marker")) { + // continue; + //} + //pluginEntry = plugins.get(pluginName); + //myPlugin = (MyPlugin) pluginEntry.plugin; + if (objects.size() > 0) { + keys = objects.keys.toArray(new String[objects.size()]); + for (j = 0; j < keys.length; j++) { + key = keys[j]; + if (key.contains("marker")) { + continue; + } + if (key.contains("property")) { + properties = (JSONObject) objects.get(key); + try { + //Log.d("PluginMap", "-----> key = " + key + ", " + properties.toString(2)); + //Log.d("PluginMap", "-----> key = " + key + ", isVisible = " + properties.getBoolean("isVisible") + ", isClickable = " + properties.getBoolean("isClickable")); + // skip invisible overlay + if (!properties.getBoolean("isVisible") || + !properties.getBoolean("isClickable")) { + continue; + } + } catch (JSONException e) { + e.printStackTrace(); + } + bounds = (LatLngBounds) objects.get(key.replace("property", "bounds")); + if (bounds.contains(point)) { + //Log.d("PluginMap", "-----> add key = " + key.replace("property_", "")); + boundsHitList.put(key, objects.get(key.replace("property_", ""))); + } + + } + } + } + + //} + } catch (Exception e) { + //e.printStackTrace(); + } + + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + //Log.d(TAG, "---> onMapClick : " + activeMarker); + if (activeMarker != null) { + //Log.d(TAG, "---> activeMarker.getTag() : " + activeMarker.getTag()); + String markerTag = activeMarker.getTag() + ""; + if (markerTag.contains("markercluster")) { + if (markerTag.contains("-marker_")) { + onClusterEvent("info_close", activeMarker); + } + // } else { + // boolean useHtmlInfoWnd = activeMarker.getTitle() == null && + // activeMarker.getSnippet() == null; + // if (useHtmlInfoWnd || activeMarker.isInfoWindowShown()) { + // onInfoWindowClose(activeMarker); + // } + } + activeMarker = null; + } + Map.Entry entry; + + Set> entrySet = boundsHitList.entrySet(); + Iterator> iterator = entrySet.iterator(); + + List points ; + Point origin = new Point(); + Point hitArea = new Point(); + hitArea.x = 1; + hitArea.y = 1; + LatLng touchPoint = null; + //double threshold = calculateDistance( + // projection.fromScreenLocation(origin), + // projection.fromScreenLocation(hitArea)); + + float zIndex = -1; + float maxZIndex = -1; + Object hitOverlay = null; + Object overlay; + String key; + + while(iterator.hasNext()) { + entry = iterator.next(); + key = entry.getKey(); + overlay = entry.getValue(); + if (key.startsWith("polyline")) { + + Polyline polyline = (Polyline)overlay; + if (polyline == null) { + continue; + } + zIndex = polyline.getZIndex(); + if (zIndex < maxZIndex) { + continue; + } + + points = polyline.getPoints(); + + if (polyline.isGeodesic()) { + hitArea.x = (int)(polyline.getWidth() * density); + hitArea.y = hitArea.x; + double threshold = calculateDistance( + projection.fromScreenLocation(origin), + projection.fromScreenLocation(hitArea)); + touchPoint = isPointOnTheGeodesicLine(points, point, threshold); + if (touchPoint != null) { + hitOverlay = polyline; + maxZIndex = zIndex; + continue; + } + } else { + touchPoint = isPointOnTheLine(points, point); + if (touchPoint != null) { + hitOverlay = polyline; + maxZIndex = zIndex; + continue; + } + } + } + + if (key.startsWith("polygon")) { + Polygon polygon = (Polygon)overlay; + if (polygon == null) { + continue; + } + zIndex = polygon.getZIndex(); + if (zIndex < maxZIndex) { + continue; + } + if (isPolygonContains(polygon.getPoints(), point)) { + touchPoint = point; + hitOverlay = polygon; + maxZIndex = zIndex; + continue; + } + } + + + if (key.startsWith("circle")) { + Circle circle = (Circle)overlay; + if (circle == null) { + continue; + } + zIndex = circle.getZIndex(); + if (zIndex < maxZIndex) { + continue; + } + if (isCircleContains(circle, point)) { + touchPoint = point; + hitOverlay = circle; + maxZIndex = zIndex; + continue; + } + } + if (key.startsWith("groundoverlay")) { + GroundOverlay groundOverlay = (GroundOverlay)overlay; + if (groundOverlay == null) { + continue; + } + zIndex = groundOverlay.getZIndex(); + if (zIndex < maxZIndex) { + continue; + } + if (isGroundOverlayContains(groundOverlay, point)) { + touchPoint = point; + hitOverlay = groundOverlay; + maxZIndex = zIndex; + //continue; + } + } + } + + + final Object finalHitOverlay = hitOverlay; + final LatLng finalTouchPoint = touchPoint; + + //Log.d("PluginMap", "---> hitOverlay = " + finalHitOverlay); + if (finalHitOverlay instanceof Polygon) { + onPolygonClick((Polygon)finalHitOverlay, finalTouchPoint); + } else if (finalHitOverlay instanceof Polyline) { + onPolylineClick((Polyline)finalHitOverlay, finalTouchPoint); + } else if (finalHitOverlay instanceof Circle) { + onCircleClick((Circle)finalHitOverlay, finalTouchPoint); + } else if (finalHitOverlay != null) { + onGroundOverlayClick((GroundOverlay)finalHitOverlay, finalTouchPoint); + } else { + // Only emit click event if no overlays are hit + onMapEvent("map_click", point); + } + } + }); + } + }); + } + + public void onRequestPermissionResult(int requestCode, String[] permissions, + int[] grantResults) throws JSONException { + //Log.d(TAG, "---> onRequestPermissionResult"); + + synchronized (semaphore) { + semaphore.notify(); + } + } + + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMarker.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMarker.java new file mode 100644 index 0000000..0c9f6f7 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMarker.java @@ -0,0 +1,1267 @@ +package plugin.google.maps; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.SystemClock; +import android.util.Log; +import android.view.animation.BounceInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import com.google.android.gms.maps.Projection; +import com.google.android.gms.maps.model.BitmapDescriptor; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.MarkerOptions; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaWebView; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.Semaphore; + +public class PluginMarker extends MyPlugin implements MyPluginInterface { + + private enum Animation { + DROP, + BOUNCE + } + + protected HashMap iconLoadingTasks = new HashMap(); + protected HashMap icons = new HashMap(); + protected final HashMap iconCacheKeys = new HashMap(); + private static final Paint paint = new Paint(); + private boolean _clearDone = false; + + protected interface ICreateMarkerCallback { + void onSuccess(Marker marker); + + void onError(String message); + } + + @Override + public void initialize(CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + } + + @Override + public void onDestroy() { + super.onDestroy(); + this.clear(); + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Set keySet = pluginMap.objects.keys; + if (keySet.size() > 0) { + String[] objectIdArray = keySet.toArray(new String[keySet.size()]); + + for (String objectId : objectIdArray) { + if (pluginMap.objects.containsKey(objectId)) { + if (objectId.startsWith("marker_") && + !objectId.startsWith("marker_property_") && + !objectId.startsWith("marker_imageSize_") && + !objectId.startsWith("marker_icon_")) { + Marker marker = (Marker) pluginMap.objects.remove(objectId); + _removeMarker(marker); + marker = null; + } else { + Object object = pluginMap.objects.remove(objectId); + object = null; + } + } + } + } + + pluginMap.objects.clear(); + } + }); + + } + + @Override + protected void clear() { + + Semaphore semaphore = new Semaphore(1); + + _clearDone = false; + + cordova.getThreadPool().submit(new Runnable() { + @Override + public void run() { + //-------------------------------------- + // Cancel tasks + //-------------------------------------- + AsyncTask task; + if (iconLoadingTasks != null && iconLoadingTasks.size() > 0) { + int i, ilen = iconLoadingTasks.size(); + for (i = 0; i < ilen; i++) { + task = iconLoadingTasks.get(i); + task.cancel(true); + } + } + } + }); + + + //-------------------------------------- + // Recycle bitmaps as much as possible + //-------------------------------------- + if (iconCacheKeys != null) { + if (iconCacheKeys.size() > 0) { + String[] cacheKeys = iconCacheKeys.keySet().toArray(new String[iconCacheKeys.size()]); + for (int i = 0; i < cacheKeys.length; i++) { + AsyncLoadImage.removeBitmapFromMemCahce(cacheKeys[i]); + iconCacheKeys.remove(cacheKeys[i]); + } + cacheKeys = null; + } + } + if (icons != null && icons.size() > 0) { + String[] keys = icons.keySet().toArray(new String[icons.size()]); + //Bitmap[] cachedBitmaps = icons.toArray(new Bitmap[icons.size()]); + Bitmap image; + for (int i = 0; i < keys.length; i++) { + image = icons.remove(keys[i]); + if (image != null && !image.isRecycled()) { + image.recycle(); + } + image = null; + } + icons.clear(); + } + + try { + + semaphore.acquire(); + //-------------------------------------- + // clean up properties as much as possible + //-------------------------------------- + cordova.getActivity().runOnUiThread(() -> { + Set keySet = pluginMap.objects.keys; + if (keySet.size() > 0) { + String[] objectIdArray = keySet.toArray(new String[keySet.size()]); + + for (String objectId : objectIdArray) { + if (pluginMap.objects.containsKey(objectId)) { + if (objectId.startsWith("marker_") && + !objectId.startsWith("marker_property_") && + !objectId.startsWith("marker_imageSize") && + !objectId.startsWith("marker_icon_")) { + Marker marker = (Marker) pluginMap.objects.remove(objectId); + marker.setTag(null); + marker.remove(); + marker = null; + } else { + Object object = pluginMap.objects.remove(objectId); + object = null; + } + } + } + } + + _clearDone = true; + semaphore.release(); + }); + + if (!_clearDone) { + semaphore.acquire(); + } + } catch (InterruptedException ignore) { + + } + } + + /** + * Create a marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + // Create an instance of Marker class + + JSONObject opts = args.getJSONObject(1); + final String markerId = "marker_" + args.getString(2); + final JSONObject result = new JSONObject(); + result.put("__pgmId", markerId); + + _create(markerId, opts, new ICreateMarkerCallback() { + @Override + public void onSuccess(Marker marker) { + if (icons.containsKey(markerId)) { + Bitmap icon = icons.get(markerId); + try { + result.put("width", icon.getWidth() / density); + result.put("height", icon.getHeight() / density); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + try { + result.put("width", 24); + result.put("height", 42); + } catch (Exception e) { + e.printStackTrace(); + } + } + + callbackContext.success(result); + } + + @Override + public void onError(String message) { + callbackContext.error(message); + } + }); + } + + protected void _create(final String markerId, final JSONObject opts, final ICreateMarkerCallback callback) throws JSONException { + final JSONObject properties = new JSONObject(); + final MarkerOptions markerOptions = new MarkerOptions(); + if (opts.has("position")) { + JSONObject position = opts.getJSONObject("position"); + markerOptions.position(new LatLng(position.getDouble("lat"), position.getDouble("lng"))); + } + if (opts.has("title")) { + markerOptions.title(opts.getString("title")); + } + if (opts.has("snippet")) { + markerOptions.snippet(opts.getString("snippet")); + } + if (opts.has("visible")) { + if (opts.has("icon") && !"".equals(opts.getString("icon"))) { + markerOptions.visible(false); + properties.put("isVisible", false); + } else { + markerOptions.visible(opts.getBoolean("visible")); + properties.put("isVisible", markerOptions.isVisible()); + } + } else { + markerOptions.visible(true); + } + if (opts.has("draggable")) { + markerOptions.draggable(opts.getBoolean("draggable")); + } + if (opts.has("rotation")) { + markerOptions.rotation((float) opts.getDouble("rotation")); + } + if (opts.has("flat")) { + markerOptions.flat(opts.getBoolean("flat")); + } + if (opts.has("opacity")) { + markerOptions.alpha((float) opts.getDouble("opacity")); + } + if (opts.has("zIndex")) { + markerOptions.zIndex((float) opts.getDouble("zIndex")); + } + + if (opts.has("styles")) { + properties.put("styles", opts.getJSONObject("styles")); + } + if (opts.has("disableAutoPan")) { + properties.put("disableAutoPan", opts.getBoolean("disableAutoPan")); + } else { + properties.put("disableAutoPan", false); + } + if (opts.has("noCache")) { + properties.put("noCache", opts.getBoolean("noCache")); + } else { + properties.put("noCache", false); + } + if (opts.has("useHtmlInfoWnd")) { + properties.put("useHtmlInfoWnd", opts.getBoolean("useHtmlInfoWnd")); + } else { + properties.put("useHtmlInfoWnd", false); + } + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + final Marker marker = map.addMarker(markerOptions); + marker.setTag(markerId); + marker.hideInfoWindow(); + + cordova.getThreadPool().execute(new Runnable() { + @Override + public void run() { + + try { + // Store the marker + synchronized (pluginMap.objects) { + pluginMap.objects.put(markerId, marker); + + pluginMap.objects.put("marker_property_" + markerId, properties); + } + + // Prepare the result + final JSONObject result = new JSONObject(); + result.put("__pgmId", markerId); + + // Load icon + if (opts.has("icon")) { + //------------------------------ + // Case: have the icon property + //------------------------------ + Bundle bundle = null; + Object value = opts.get("icon"); + if (JSONObject.class.isInstance(value)) { + JSONObject iconProperty = (JSONObject) value; + if (iconProperty.has("url") && JSONArray.class.isInstance(iconProperty.get("url"))) { + + float[] hsv = new float[3]; + JSONArray arrayRGBA = iconProperty.getJSONArray("url"); + Color.RGBToHSV(arrayRGBA.getInt(0), arrayRGBA.getInt(1), arrayRGBA.getInt(2), hsv); + bundle = new Bundle(); + bundle.putFloat("iconHue", hsv[0]); + + } else { + if (iconProperty.has("label")) { + JSONObject label = iconProperty.getJSONObject("label"); + if (label != null && label.get("color") instanceof JSONArray) { + label.put("color", PluginUtil.parsePluginColor(label.getJSONArray("color"))); + iconProperty.put("label", label); + } + } + bundle = PluginUtil.Json2Bundle(iconProperty); + + // The `anchor` of the `icon` property + if (iconProperty.has("anchor")) { + value = iconProperty.get("anchor"); + if (JSONArray.class.isInstance(value)) { + JSONArray points = (JSONArray) value; + double[] anchorPoints = new double[points.length()]; + for (int i = 0; i < points.length(); i++) { + anchorPoints[i] = points.getDouble(i); + } + bundle.putDoubleArray("anchor", anchorPoints); + } else if (value instanceof JSONObject && ((JSONObject) value).has("x") && ((JSONObject) value).has("y")) { + double[] anchorPoints = new double[2]; + anchorPoints[0] = ((JSONObject) value).getDouble("x"); + anchorPoints[1] = ((JSONObject) value).getDouble("y"); + bundle.putDoubleArray("anchor", anchorPoints); + } + } + + // The `infoWindowAnchor` property for infowindow + if (opts.has("infoWindowAnchor")) { + value = opts.get("infoWindowAnchor"); + if (JSONArray.class.isInstance(value)) { + JSONArray points = (JSONArray) value; + double[] anchorPoints = new double[points.length()]; + for (int i = 0; i < points.length(); i++) { + anchorPoints[i] = points.getDouble(i); + } + bundle.putDoubleArray("infoWindowAnchor", anchorPoints); + } else if (value instanceof JSONObject && ((JSONObject) value).has("x") && ((JSONObject) value).has("y")) { + double[] anchorPoints = new double[2]; + anchorPoints[0] = ((JSONObject) value).getDouble("x"); + anchorPoints[1] = ((JSONObject) value).getDouble("y"); + bundle.putDoubleArray("infoWindowAnchor", anchorPoints); + } + } + } + + } else if (JSONArray.class.isInstance(value)) { + float[] hsv = new float[3]; + JSONArray arrayRGBA = (JSONArray) value; + Color.RGBToHSV(arrayRGBA.getInt(0), arrayRGBA.getInt(1), arrayRGBA.getInt(2), hsv); + bundle = new Bundle(); + bundle.putFloat("iconHue", hsv[0]); + } else { + bundle = new Bundle(); + bundle.putString("url", (String) value); + } + + if (opts.has("animation")) { + bundle.putString("animation", opts.getString("animation")); + } + PluginMarker.this.setIcon_(marker, bundle, new PluginAsyncInterface() { + + @Override + public void onPostExecute(final Object object) { + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + Marker marker = (Marker) object; + if (opts.has("visible")) { + try { + marker.setVisible(opts.getBoolean("visible")); + } catch (JSONException e) { + } + } else { + marker.setVisible(true); + } + + + // Animation + String markerAnimation = null; + if (opts.has("animation")) { + try { + markerAnimation = opts.getString("animation"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + if (markerAnimation != null) { + PluginMarker.this.setMarkerAnimation_(marker, markerAnimation, new PluginAsyncInterface() { + + @Override + public void onPostExecute(Object object) { + Marker marker = (Marker) object; + callback.onSuccess(marker); + } + + @Override + public void onError(String errorMsg) { + callback.onError(errorMsg); + } + }); + } else { + callback.onSuccess(marker); + } + } + }); + } + + @Override + public void onError(String errorMsg) { + callback.onError(errorMsg); + } + + }); + } else { + //-------------------------- + // Case: no icon property + //-------------------------- + String markerAnimation = null; + if (opts.has("animation")) { + markerAnimation = opts.getString("animation"); + } + if (markerAnimation != null) { + // Execute animation + PluginMarker.this.setMarkerAnimation_(marker, markerAnimation, new PluginAsyncInterface() { + + @Override + public void onPostExecute(Object object) { + callback.onSuccess(marker); + } + + @Override + public void onError(String errorMsg) { + callback.onError(errorMsg); + } + + }); + } else { + // Return the result if does not specify the icon property. + callback.onSuccess(marker); + } + } + } catch (Exception e) { + e.printStackTrace(); + callback.onError("" + e.getMessage()); + } + } + }); + } + }); + + } + + private void setDropAnimation_(final Marker marker, final PluginAsyncInterface callback) { + final long startTime = SystemClock.uptimeMillis(); + final long duration = 100; + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + final Handler handler = new Handler(); + final Projection proj = map.getProjection(); + final LatLng markerLatLng = marker.getPosition(); + final Point markerPoint = proj.toScreenLocation(markerLatLng); + final Point startPoint = new Point(markerPoint.x, 0); + + final Interpolator interpolator = new LinearInterpolator(); + + handler.post(new Runnable() { + @Override + public void run() { + LatLng startLatLng = proj.fromScreenLocation(startPoint); + long elapsed = SystemClock.uptimeMillis() - startTime; + float t = interpolator.getInterpolation((float) elapsed / duration); + + if(startLatLng != null) { + double lng = t * markerLatLng.longitude + (1 - t) * startLatLng.longitude; + double lat = t * markerLatLng.latitude + (1 - t) * startLatLng.latitude; + marker.setPosition(new LatLng(lat, lng)); + } + + if (t < 1.0) { + // Post again 16ms later. + handler.postDelayed(this, 16); + } else { + marker.setPosition(markerLatLng); + callback.onPostExecute(marker); + } + } + }); + } + }); + } + + /** + * Bounce animation + * http://android-er.blogspot.com/2013/01/implement-bouncing-marker-for-google.html + */ + private void setBounceAnimation_(final Marker marker, final PluginAsyncInterface callback) { + final long startTime = SystemClock.uptimeMillis(); + final long duration = 2000; + final Interpolator interpolator = new BounceInterpolator(); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + final Handler handler = new Handler(); + final Projection projection = map.getProjection(); + final LatLng markerLatLng = marker.getPosition(); + final Point startPoint = projection.toScreenLocation(markerLatLng); + startPoint.offset(0, -200); + + + handler.post(new Runnable() { + @Override + public void run() { + LatLng startLatLng = projection.fromScreenLocation(startPoint); + long elapsed = SystemClock.uptimeMillis() - startTime; + float t = interpolator.getInterpolation((float) elapsed / duration); + + if(startLatLng != null) { + double lng = t * markerLatLng.longitude + (1 - t) * startLatLng.longitude; + double lat = t * markerLatLng.latitude + (1 - t) * startLatLng.latitude; + marker.setPosition(new LatLng(lat, lng)); + } + + if (t < 1.0) { + // Post again 16ms later. + handler.postDelayed(this, 16); + } else { + marker.setPosition(markerLatLng); + callback.onPostExecute(marker); + } + } + }); + } + }); + + } + + private void setMarkerAnimation_(Marker marker, String animationType, PluginAsyncInterface callback) { + Animation animation = null; + try { + animation = Animation.valueOf(animationType.toUpperCase(Locale.US)); + } catch (Exception e) { + e.printStackTrace(); + } + if (animation == null) { + callback.onPostExecute(marker); + return; + } + switch (animation) { + case DROP: + this.setDropAnimation_(marker, callback); + break; + + case BOUNCE: + this.setBounceAnimation_(marker, callback); + break; + + default: + break; + } + } + + /** + * http://android-er.blogspot.com/2013/01/implement-bouncing-marker-for-google.html + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setAnimation(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String markerId = args.getString(0); + String animation = args.getString(1); + final Marker marker = this.getMarker(markerId); + Log.d(TAG, "--->setAnimation: markerId = " + markerId + ", animation = " + animation); + if (marker == null) { + callbackContext.error("marker is null"); + return; + } + + this.setMarkerAnimation_(marker, animation, new PluginAsyncInterface() { + + @Override + public void onPostExecute(Object object) { + callbackContext.success(); + } + + @Override + public void onError(String errorMsg) { + callbackContext.error(errorMsg); + } + + }); + } + + /** + * Show the InfoWindow bound with the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void showInfoWindow(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String id = args.getString(0); + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Marker marker = getMarker(id); + if (marker != null) { + marker.showInfoWindow(); + } + callbackContext.success(); + } + }); + } + + /** + * Set rotation for the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setRotation(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + float rotation = (float) args.getDouble(1); + String id = args.getString(0); + this.setFloat("setRotation", id, rotation, callbackContext); + } + + /** + * Set opacity for the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setOpacity(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + float alpha = (float) args.getDouble(1); + String id = args.getString(0); + this.setFloat("setAlpha", id, alpha, callbackContext); + } + + /** + * Set zIndex for the marker (dummy code, not available on Android V2) + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + float zIndex = (float) args.getDouble(1); + String id = args.getString(0); + Marker marker = getMarker(id); + this.setFloat("setZIndex", id, zIndex, callbackContext); + } + + /** + * set position + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setPosition(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String id = args.getString(0); + final LatLng position = new LatLng(args.getDouble(1), args.getDouble(2)); + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Marker marker = getMarker(id); + if (marker != null) { + marker.setPosition(position); + } + callbackContext.success(); + } + }); + } + + /** + * Set flat for the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setFlat(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + boolean isFlat = args.getBoolean(1); + String id = args.getString(0); + this.setBoolean("setFlat", id, isFlat, callbackContext); + } + + /** + * Set visibility for the object + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException { + boolean isVisible = args.getBoolean(1); + String id = args.getString(0); + + Marker marker = this.getMarker(id); + if (marker == null) { + callbackContext.success(); + return; + } + String propertyId = "marker_property_" + id; + JSONObject properties = null; + if (self.pluginMap.objects.containsKey(propertyId)) { + properties = (JSONObject) self.pluginMap.objects.get(propertyId); + } else { + properties = new JSONObject(); + } + properties.put("isVisible", isVisible); + self.pluginMap.objects.put(propertyId, properties); + + this.setBoolean("setVisible", id, isVisible, callbackContext); + } + + /** + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setDisableAutoPan(JSONArray args, CallbackContext callbackContext) throws JSONException { + boolean disableAutoPan = args.getBoolean(1); + String id = args.getString(0); + Marker marker = this.getMarker(id); + if (marker == null) { + callbackContext.success(); + return; + } + String propertyId = "marker_property_" + id; + JSONObject properties = null; + if (self.pluginMap.objects.containsKey(propertyId)) { + properties = (JSONObject) self.pluginMap.objects.get(propertyId); + } else { + properties = new JSONObject(); + } + properties.put("disableAutoPan", disableAutoPan); + self.pluginMap.objects.put(propertyId, properties); + callbackContext.success(); + } + + /** + * Set title for the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setTitle(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String title = args.getString(1); + String id = args.getString(0); + this.setString("setTitle", id, title, callbackContext); + } + + /** + * Set the snippet for the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setSnippet(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String snippet = args.getString(1); + String id = args.getString(0); + this.setString("setSnippet", id, snippet, callbackContext); + } + + /** + * Hide the InfoWindow binded with the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void hideInfoWindow(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String id = args.getString(0); + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Marker marker = getMarker(id); + if (marker != null) { + marker.hideInfoWindow(); + } + callbackContext.success(); + } + }); + } + + /** + * Remove the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final String id = args.getString(0); + final Marker marker = this.getMarker(id); + if (marker == null) { + callbackContext.success(); + return; + } + + /* + String[] cacheKeys = iconCacheKeys.toArray(new String[iconCacheKeys.size()]); + for (int i = 0; i < cacheKeys.length; i++) { + AsyncLoadImage.removeBitmapFromMemCahce(cacheKeys[i]); + } + */ + + String propertyId = "marker_property_" + id; + pluginMap.objects.remove(propertyId); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + pluginMap.objects.remove(id); + _removeMarker(marker); + + callbackContext.success(); + } + }); + } + + protected void _removeMarker(Marker marker) { + if (marker == null || marker.getTag() == null) { + return; + } + //--------------------------------------------- + // Removes marker safely + // (prevent the `un-managed object exception`) + //--------------------------------------------- + String iconCacheKey = "marker_icon_" + marker.getTag(); + marker.setTag(null); + marker.remove(); + + //--------------------------------------------------------------------------------- + // If no marker uses the icon image used be specified this marker, release it + //--------------------------------------------------------------------------------- + if (pluginMap.objects.containsKey(iconCacheKey)) { + String cacheKey = (String) pluginMap.objects.remove(iconCacheKey); + if (iconCacheKeys.containsKey(cacheKey)) { + int count = iconCacheKeys.get(cacheKey); + count--; + if (count < 1) { + AsyncLoadImage.removeBitmapFromMemCahce(cacheKey); + iconCacheKeys.remove(cacheKey); + } else { + iconCacheKeys.put(cacheKey, count); + } + } + pluginMap.objects.remove(iconCacheKey); + } + } + + /** + * Set anchor for the icon of the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setIconAnchor(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + float anchorX = (float) args.getDouble(1); + float anchorY = (float) args.getDouble(2); + String id = args.getString(0); + Marker marker = this.getMarker(id); + + Bundle imageSize = (Bundle) self.pluginMap.objects.get("marker_imageSize_" + id); + if (imageSize != null) { + this._setIconAnchor(marker, anchorX, anchorY, imageSize.getInt("width"), imageSize.getInt("height")); + } + callbackContext.success(); + } + + + /** + * Set anchor for the InfoWindow of the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setInfoWindowAnchor(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + float anchorX = (float) args.getDouble(1); + float anchorY = (float) args.getDouble(2); + String id = args.getString(0); + Marker marker = this.getMarker(id); + + Bundle imageSize = (Bundle) self.pluginMap.objects.get("marker_imageSize_" + id); + if (imageSize != null) { + this._setInfoWindowAnchor(marker, anchorX, anchorY, imageSize.getInt("width"), imageSize.getInt("height")); + } + callbackContext.success(); + } + + /** + * Set draggable for the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setDraggable(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + Boolean draggable = args.getBoolean(1); + String id = args.getString(0); + this.setBoolean("setDraggable", id, draggable, callbackContext); + } + + /** + * Set icon of the marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setIcon(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + Marker marker = this.getMarker(id); + Object value = args.get(1); + Bundle bundle = null; + if (JSONObject.class.isInstance(value)) { + JSONObject iconProperty = (JSONObject) value; + bundle = PluginUtil.Json2Bundle(iconProperty); + + // The `anchor` for icon + if (iconProperty.has("anchor")) { + value = iconProperty.get("anchor"); + if (JSONArray.class.isInstance(value)) { + JSONArray points = (JSONArray) value; + double[] anchorPoints = new double[points.length()]; + for (int i = 0; i < points.length(); i++) { + anchorPoints[i] = points.getDouble(i); + } + bundle.putDoubleArray("anchor", anchorPoints); + } else if (value instanceof JSONObject && ((JSONObject) value).has("x") && ((JSONObject) value).has("y")) { + double[] anchorPoints = new double[2]; + anchorPoints[0] = ((JSONObject) value).getDouble("x"); + anchorPoints[1] = ((JSONObject) value).getDouble("y"); + bundle.putDoubleArray("anchor", anchorPoints); + } + } + } else if (JSONArray.class.isInstance(value)) { + float[] hsv = new float[3]; + JSONArray arrayRGBA = (JSONArray) value; + Color.RGBToHSV(arrayRGBA.getInt(0), arrayRGBA.getInt(1), arrayRGBA.getInt(2), hsv); + bundle = new Bundle(); + bundle.putFloat("iconHue", hsv[0]); + } else if (String.class.isInstance(value)) { + bundle = new Bundle(); + bundle.putString("url", (String) value); + } + if (bundle != null) { + this.setIcon_(marker, bundle, new PluginAsyncInterface() { + + @Override + public void onPostExecute(Object object) { + callbackContext.success(); + } + + @Override + public void onError(String errorMsg) { + callbackContext.error(errorMsg); + } + }); + } else { + callbackContext.success(); + } + } + + protected void setIcon_(final Marker marker, final Bundle iconProperty, final PluginAsyncInterface callback) { + boolean noCaching = false; + if (iconProperty.containsKey("noCache")) { + noCaching = iconProperty.getBoolean("noCache"); + } + if (iconProperty.containsKey("iconHue")) { + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + float hue = iconProperty.getFloat("iconHue"); + marker.setIcon(BitmapDescriptorFactory.defaultMarker(hue)); + } + }); + callback.onPostExecute(marker); + return; + } + + String iconUrl = iconProperty.getString("url"); + if (iconUrl == null) { + callback.onPostExecute(marker); + return; + } + + int width = -1; + int height = -1; + if (iconProperty.containsKey("size")) { + Bundle sizeInfo = (Bundle) iconProperty.get("size"); + width = sizeInfo.getInt("width", width); + height = sizeInfo.getInt("height", height); + } + + + final AsyncLoadImage.AsyncLoadImageOptions options = new AsyncLoadImage.AsyncLoadImageOptions(); + options.url = iconUrl; + options.width = width; + options.height = height; + options.noCaching = noCaching; + final int taskId = options.hashCode(); + + final AsyncLoadImageInterface onComplete = new AsyncLoadImageInterface() { + @Override + public void onPostExecute(AsyncLoadImage.AsyncLoadImageResult result) { + iconLoadingTasks.remove(taskId); + + if (result == null || result.image == null) { + callback.onPostExecute(marker); + return; + } + if (marker == null) { + callback.onError("marker is removed"); + return; + } + if (result.image.isRecycled()) { + //Maybe the task was canceled by map.clean()? + callback.onError("Can not get image for marker. Maybe the task was canceled by map.clean()?"); + return; + } + + synchronized (marker) { + String markerTag = marker.getTag() + ""; + String markerIconTag = "marker_icon_" + markerTag; + String markerImgSizeTag = "marker_imageSize_" + markerTag; + + String currentCacheKey = (String) pluginMap.objects.get(markerIconTag); + if (result.cacheKey != null && result.cacheKey.equals(currentCacheKey)) { + synchronized (iconCacheKeys) { + if (iconCacheKeys.containsKey(currentCacheKey)) { + int count = iconCacheKeys.get(currentCacheKey); + count--; + if (count < 1) { + AsyncLoadImage.removeBitmapFromMemCahce(currentCacheKey); + iconCacheKeys.remove(currentCacheKey); + } else { + iconCacheKeys.put(currentCacheKey, count); + } + } + } + } + + if (icons.containsKey(markerIconTag)) { + Bitmap icon = icons.remove(markerIconTag); + if (icon != null && !icon.isRecycled()) { + icon.recycle(); + } + icon = null; + } + icons.put(markerTag, result.image); + + //------------------------------------------------------- + // Counts up the markers that use the same icon image. + //------------------------------------------------------- + if (result.cacheHit) { + if (marker == null || marker.getTag() == null) { + callback.onPostExecute(marker); + return; + } + String hitCountKey = markerIconTag; + pluginMap.objects.put(hitCountKey, result.cacheKey); + if (!iconCacheKeys.containsKey(result.cacheKey)) { + iconCacheKeys.put(result.cacheKey, 1); + } else { + int count = iconCacheKeys.get(result.cacheKey); + iconCacheKeys.put(result.cacheKey, count + 1); + } + //Log.d(TAG, "----> " + result.cacheKey + " = " + iconCacheKeys.get(result.cacheKey)); + } + + //------------------------ + // Draw label on icon + //------------------------ + if (iconProperty.containsKey("label")) { + result.image = drawLabel(result.image, iconProperty.getBundle("label")); + } + BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(result.image); + if (bitmapDescriptor == null || marker == null || marker.getTag() == null) { + callback.onPostExecute(marker); + return; + } + + //------------------------ + // Sets image as icon + //------------------------ + marker.setIcon(bitmapDescriptor); + bitmapDescriptor = null; + + //--------------------------------------------- + // Save the information for the anchor property + //--------------------------------------------- + Bundle imageSize = new Bundle(); + imageSize.putInt("width", result.image.getWidth()); + imageSize.putInt("height", result.image.getHeight()); + self.pluginMap.objects.remove(markerImgSizeTag); + self.pluginMap.objects.put(markerImgSizeTag, imageSize); + + result.image.recycle(); + result.image = null; + + // The `anchor` of the `icon` property + if (iconProperty.containsKey("anchor")) { + double[] anchor = iconProperty.getDoubleArray("anchor"); + if (anchor != null && anchor.length == 2) { + _setIconAnchor(marker, anchor[0], anchor[1], imageSize.getInt("width"), imageSize.getInt("height")); + } + } + + + // The `anchor` property for the infoWindow + if (iconProperty.containsKey("infoWindowAnchor")) { + double[] anchor = iconProperty.getDoubleArray("infoWindowAnchor"); + if (anchor != null && anchor.length == 2) { + _setInfoWindowAnchor(marker, anchor[0], anchor[1], imageSize.getInt("width"), imageSize.getInt("height")); + } + } + + callback.onPostExecute(marker); + } + } + }; + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + AsyncLoadImage task = new AsyncLoadImage(cordova, webView, options, onComplete); + task.execute(); + iconLoadingTasks.put(taskId, task); + } + }); + } + + + private void _setIconAnchor(final Marker marker, double anchorX, double anchorY, final int imageWidth, final int imageHeight) { + // The `anchor` of the `icon` property + anchorX = anchorX * density; + anchorY = anchorY * density; + final double fAnchorX = anchorX; + final double fAnchorY = anchorY; + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + marker.setAnchor((float) (fAnchorX / imageWidth), (float) (fAnchorY / imageHeight)); + } + }); + } + + private void _setInfoWindowAnchor(final Marker marker, double anchorX, double anchorY, final int imageWidth, final int imageHeight) { + // The `anchor` of the `icon` property + anchorX = anchorX * density; + anchorY = anchorY * density; + final double fAnchorX = anchorX; + final double fAnchorY = anchorY; + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + marker.setInfoWindowAnchor((float) (fAnchorX / imageWidth), (float) (fAnchorY / imageHeight)); + } + }); + } + + protected Bitmap drawLabel(Bitmap image, Bundle labelOptions) { + String text = labelOptions.getString("text"); + if (text == null || text.length() == 0) { + return image; + } + Bitmap newIcon = Bitmap.createBitmap(image); + Canvas canvas = new Canvas(newIcon); + image.recycle(); + image = null; + + int fontSize = 10; + if (labelOptions.containsKey("fontSize")) { + fontSize = labelOptions.getInt("fontSize"); + } + paint.setTextSize(fontSize * density); + + int color = Color.BLACK; + if (labelOptions.containsKey("color")) { + color = labelOptions.getInt("color"); + } + boolean bold = false; + if (labelOptions.containsKey("bold")) { + bold = labelOptions.getBoolean("bold"); + } + paint.setFakeBoldText(bold); + boolean italic = false; + if (labelOptions.containsKey("italic")) { + italic = labelOptions.getBoolean("italic"); + } + if (italic && bold) { + paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC)); + } else if (italic) { + paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)); + } else if (bold) { + paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); + } else { + paint.setTypeface(Typeface.DEFAULT); + } + paint.setColor(color); + + + Rect rect = new Rect(); + canvas.getClipBounds(rect); + int cHeight = rect.height(); + int cWidth = rect.width(); + paint.setTextAlign(Paint.Align.LEFT); + paint.getTextBounds(text, 0, text.length(), rect); + float x = cWidth / 2f - rect.width() / 2f - rect.left; + float y = cHeight / 2f + rect.height() / 2f - rect.bottom; + canvas.drawText(text, x, y, paint); + return newIcon; + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMarkerCluster.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMarkerCluster.java new file mode 100644 index 0000000..f9a220e --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginMarkerCluster.java @@ -0,0 +1,779 @@ +package plugin.google.maps; + +import android.graphics.Bitmap; +import android.os.Bundle; +import android.util.Log; + +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.MarkerOptions; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaWebView; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class PluginMarkerCluster extends PluginMarker { + + private final static Map pluginMarkers = new ConcurrentHashMap(); + private final static Map waitCntManager = new ConcurrentHashMap(); + private final static Map debugFlags = new ConcurrentHashMap(); + private final static ArrayList deleteMarkers = new ArrayList(); + + private final Object semaphore = new Object(); + private boolean stopFlag = false; + private final Object deleteThreadLock = new Object(); + + enum STATUS { + WORKING, + CREATED, + DELETED + } + + @Override + public void initialize(CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + if (!deleteThread.isAlive()) { + deleteThread.start(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + stopFlag = true; + } + + @Override + protected void clear() { + super.clear(); + synchronized (semaphore) { + synchronized (pluginMarkers) { + synchronized (deleteMarkers) { + String clusterId_markerId; + if (pluginMarkers.size() > 0) { + String[] keys = pluginMarkers.keySet().toArray(new String[pluginMarkers.size()]); + for (int i = 0; i < keys.length; i++) { + clusterId_markerId = keys[i]; + pluginMarkers.put(clusterId_markerId, STATUS.DELETED); + deleteMarkers.add(clusterId_markerId); + } + } + } + } + } + } + + //--------------------- + // Delete thread + //--------------------- + private Thread deleteThread = new Thread(new Runnable() { + @Override + public void run() { + while(!stopFlag) { + synchronized (deleteThreadLock) { + try { + deleteThreadLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (deleteMarkers.size() == 0) { + continue; + } + + cordova.getActivity().runOnUiThread(removeOverlaysOnUiThread); + } + } + }); + + private Runnable removeOverlaysOnUiThread = new Runnable() { + @Override + public void run() { + synchronized (deleteMarkers) { + String markerId; + Marker marker; + STATUS status; + String cacheKey; + if (deleteMarkers.size() > 0) { + String[] targetIDs = deleteMarkers.toArray(new String[deleteMarkers.size()]); + + for (int i = targetIDs.length - 1; i > -1; i--) { + markerId = targetIDs[i]; + + marker = self.getMarker(markerId); + synchronized (pluginMarkers) { + status = pluginMarkers.get(markerId); + + if (!STATUS.WORKING.equals(status)) { + synchronized (pluginMap.objects) { + _removeMarker(marker); + marker = null; + + cacheKey = (String) pluginMap.objects.remove("marker_icon_" + markerId); + if (cacheKey != null && iconCacheKeys.containsKey(cacheKey)) { + int count = iconCacheKeys.get(cacheKey); + if (count < 1) { + iconCacheKeys.remove(cacheKey); + AsyncLoadImage.removeBitmapFromMemCahce(cacheKey); + } else { + iconCacheKeys.put(cacheKey, count - 1); + } + } + + + pluginMap.objects.remove(markerId); + pluginMap.objects.remove("marker_property_" + markerId); + pluginMap.objects.remove("marker_imageSize_" + markerId); + } + pluginMarkers.remove(markerId); + deleteMarkers.remove(i); + } else { + pluginMarkers.put(markerId, STATUS.DELETED); + } + } + } + } + } + System.gc(); + } + }; + + + public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String clusterId = args.getString(0); + //Log.d(TAG, "-->remove = " + clusterId); + synchronized (debugFlags) { + debugFlags.remove(clusterId); + waitCntManager.remove(clusterId); + } + synchronized (pluginMarkers) { + for(String key: pluginMarkers.keySet()) { + if (key.startsWith(clusterId)) { + //Log.d(TAG, "-->delete = " + key); + pluginMarkers.put(key, STATUS.CREATED); + deleteMarkers.add(key); + } + } + } + + /* + String cacheKey; + Set keySet = pluginMap.objects.keySet(); + Marker marker; + String[] objectIdArray = keySet.toArray(new String[keySet.size()]); + for (String objectId : objectIdArray) { + if (objectId.contains(clusterId)) { + if (objectId.startsWith("marker_icon")) { + cacheKey = (String) pluginMap.objects.remove(objectId); + + if (iconCacheKeys.containsKey(cacheKey)) { + int count = iconCacheKeys.get(cacheKey); + if (count < 1) { + iconCacheKeys.remove(cacheKey); + AsyncLoadImage.removeBitmapFromMemCahce(cacheKey); + } else { + iconCacheKeys.put(cacheKey, count - 1); + } + } + } else if (objectId.startsWith("marker_property_") || + objectId.startsWith("marker_imageSize_")) { + pluginMap.objects.remove(objectId); + //} else if (objectId.startsWith("marker_")) { + // marker = (Marker) pluginMap.objects.remove(objectId); + // marker.remove(); + } else { + pluginMap.objects.remove(objectId); + } + } + } + */ + synchronized (deleteThreadLock) { + deleteThreadLock.notify(); + } + + callbackContext.success(); + } + /** + * Create a marker + * + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + JSONObject params = args.getJSONObject(1); + String hashCode = args.getString(2); + JSONArray positionList = params.getJSONArray("positionList"); + JSONArray geocellList = new JSONArray(); + JSONObject position; + + for (int i = 0; i < positionList.length(); i++) { + position = positionList.getJSONObject(i); + geocellList.put(getGeocell(position.getDouble("lat"), position.getDouble("lng"), 12)); + } + + String id = "markercluster_" + hashCode; + debugFlags.put(id, params.getBoolean("debug")); + + final JSONObject result = new JSONObject(); + try { + result.put("geocellList", geocellList); + result.put("hashCode", hashCode); + result.put("__pgmId", id); + } catch (JSONException e) { + e.printStackTrace(); + } + + callbackContext.success(result); + } + + + public void redrawClusters(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + + final HashSet updateClusterIDs = new HashSet(); + final HashMap changeProperties = new HashMap(); + final String clusterId = args.getString(0); + final boolean isDebug = debugFlags.get(clusterId); + final JSONObject params = args.getJSONObject(1); + + String markerId, clusterId_markerId; + JSONArray new_or_update = null; + if (params.has("new_or_update")) { + new_or_update = params.getJSONArray("new_or_update"); + } + + //--------------------------- + // Determine new or update + //--------------------------- + int new_or_updateCnt = 0; + if (new_or_update != null) { + new_or_updateCnt = new_or_update.length(); + } + JSONObject clusterData, positionJSON; + Bundle properties; + boolean isNew; + for (int i = 0; i < new_or_updateCnt; i++) { + clusterData = new_or_update.getJSONObject(i); + positionJSON = clusterData.getJSONObject("position"); + markerId = clusterData.getString("__pgmId"); + clusterId_markerId = clusterId + "-" + markerId; + + // Save the marker properties + pluginMap.objects.put("marker_property_" + clusterId_markerId, clusterData); + + // Set the WORKING status flag + synchronized (pluginMarkers) { + pluginMarkers.put(clusterId_markerId, STATUS.WORKING); + } + updateClusterIDs.add(clusterId_markerId); + + // Prepare the marker properties for addMarker() + properties = new Bundle(); + properties.putDouble("lat", positionJSON.getDouble("lat")); + properties.putDouble("lng", positionJSON.getDouble("lng")); + if (clusterData.has("title")) { + properties.putString("title", clusterData.getString("title")); + } + properties.putString("__pgmId", clusterId_markerId); + + if (clusterData.has("icon")) { + Object iconObj = clusterData.get("icon"); + + if (iconObj instanceof String) { + Bundle iconProperties = new Bundle(); + iconProperties.putString("url", (String) iconObj); + properties.putBundle("icon", iconProperties); + + } else if (iconObj instanceof JSONObject) { + JSONObject icon = clusterData.getJSONObject("icon"); + Bundle iconProperties = PluginUtil.Json2Bundle(icon); + if (clusterData.has("isClusterIcon") && clusterData.getBoolean("isClusterIcon")) { + if (icon.has("label")) { + JSONObject label = icon.getJSONObject("label"); + if (isDebug) { + label.put("text", markerId); + } else { + label.put("text", clusterData.getInt("count") + ""); + } + if (label.has("color")) { + label.put("color", PluginUtil.parsePluginColor(label.getJSONArray("color"))); + } + iconProperties.putBundle("label", PluginUtil.Json2Bundle(label)); + } else { + Bundle label = new Bundle(); + if (isDebug) { + label.putString("text", markerId); + } else { + label.putInt("fontSize", 15); + label.putBoolean("bold", true); + label.putString("text", clusterData.getInt("count") + ""); + } + iconProperties.putBundle("label", label); + } + } + if (icon.has("anchor")) { + double[] anchor = new double[2]; + anchor[0] = icon.getJSONArray("anchor").getDouble(0); + anchor[1] = icon.getJSONArray("anchor").getDouble(1); + iconProperties.putDoubleArray("anchor", anchor); + } + if (icon.has("infoWindowAnchor")) { + double[] anchor = new double[2]; + anchor[0] = icon.getJSONArray("infoWindowAnchor").getDouble(0); + anchor[1] = icon.getJSONArray("infoWindowAnchor").getDouble(1); + iconProperties.putDoubleArray("infoWindowAnchor", anchor); + } + properties.putBundle("icon", iconProperties); + } + } + + changeProperties.put(clusterId_markerId, properties); + } + + //Log.d(TAG, "---> deleteCnt : " + deleteCnt + ", newCnt : " + newCnt + ", updateCnt : " + updateCnt + ", reuseCnt : " + reuseCnt); + + if (updateClusterIDs.size() == 0) { + deleteProcess(clusterId, params); + callbackContext.success(); + return; + } + + //--------------------------- + // mapping markers on the map + //--------------------------- + final JSONObject allResults = new JSONObject(); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Iterator iterator; + String clusterId_markerId; + Bundle markerProperties; + Marker marker; + Boolean isNew; + + //--------- + // new or update + //--------- + int waitCnt = updateClusterIDs.size(); + int currentCnt = 0; + waitCntManager.put(clusterId, updateClusterIDs.size()); + iterator = updateClusterIDs.iterator(); + while (iterator.hasNext()) { + currentCnt++; + clusterId_markerId = iterator.next(); + //Log.d(TAG, "---> progress = " + currentCnt + "/ " + waitCnt + ", " + clusterId_markerId); + synchronized (pluginMarkers) { + pluginMarkers.put(clusterId_markerId, STATUS.WORKING); + } + isNew = !pluginMap.objects.containsKey(clusterId_markerId); + + // Get the marker properties + markerProperties = changeProperties.get(clusterId_markerId); + if (clusterId_markerId.contains("-marker_")) { + //------------------- + // regular marker + //------------------- + if (isNew) { + final String fMarkerId = clusterId_markerId; + JSONObject properties = (JSONObject) pluginMap.objects.get("marker_property_" + clusterId_markerId); + try { + _create(clusterId_markerId, properties, new ICreateMarkerCallback() { + @Override + public void onSuccess(Marker marker) { + + synchronized (pluginMarkers) { + if (pluginMarkers.get(fMarkerId) == STATUS.DELETED) { + _removeMarker(marker); + pluginMarkers.remove(fMarkerId); + } else { + pluginMarkers.put(fMarkerId, STATUS.CREATED); + + + JSONObject result = new JSONObject(); + if (icons.containsKey(fMarkerId)) { + Bitmap icon = icons.get(fMarkerId); + try { + result.put("width", icon.getWidth() / density); + result.put("height", icon.getHeight() / density); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + try { + result.put("width", 24); + result.put("height", 42); + } catch (Exception e) { + e.printStackTrace(); + } + } + try { + allResults.put(fMarkerId.split("-")[1], result); + } catch (JSONException e) { + e.printStackTrace(); + } + } + } + decreaseWaitCnt(clusterId); + } + + @Override + public void onError(String message) { + synchronized (pluginMarkers) { + pluginMarkers.put(fMarkerId, STATUS.DELETED); + } + Log.e(TAG, message); + decreaseWaitCnt(clusterId); + synchronized (deleteMarkers) { + deleteMarkers.add(fMarkerId); + } + } + }); + } catch (JSONException e) { + e.printStackTrace(); + decreaseWaitCnt(clusterId); + } + } else { + marker = getMarker(clusterId_markerId); + //---------------------------------------- + // Set the title and snippet properties + //---------------------------------------- + if (markerProperties.containsKey("title")) { + marker.setTitle(markerProperties.getString("title")); + } + if (markerProperties.containsKey("snippet")) { + marker.setSnippet(markerProperties.getString("snippet")); + } + synchronized (pluginMarkers) { + if (pluginMarkers.get(clusterId_markerId) == STATUS.DELETED) { + _removeMarker(marker); + pluginMarkers.remove(clusterId_markerId); + } else { + pluginMarkers.put(clusterId_markerId, STATUS.CREATED); + } + } + decreaseWaitCnt(clusterId); + } + continue; + } + //-------------------------- + // cluster icon + //-------------------------- + if (isNew) { + // If the requested id is new location, create a marker + marker = map.addMarker(new MarkerOptions() + .position(new LatLng(markerProperties.getDouble("lat"), markerProperties.getDouble("lng"))) + .visible(false)); + marker.setTag(clusterId_markerId); + + // Store the marker instance with markerId + synchronized (pluginMap.objects) { + pluginMap.objects.put(clusterId_markerId, marker); + } + } else { + marker = getMarker(clusterId_markerId); + } + //---------------------------------------- + // Set the title and snippet properties + //---------------------------------------- + if (markerProperties.containsKey("title")) { + marker.setTitle(markerProperties.getString("title")); + } + if (markerProperties.containsKey("snippet")) { + marker.setSnippet(markerProperties.getString("snippet")); + } +/* + if (!isNew) { + synchronized (pluginMarkers) { + pluginMarkers.put(markerId, STATUS.CREATED); + } + decreaseWaitCnt(clusterId); + continue; + } +*/ + if (markerProperties.containsKey("icon")) { + Bundle icon = markerProperties.getBundle("icon"); + final String fMarkerId = clusterId_markerId; + //Log.d(TAG, "---> targetMarkerId = " + targetMarkerId + ", marker = " + marker); + setIconToClusterMarker(clusterId_markerId, marker, icon, new PluginAsyncInterface() { + @Override + public void onPostExecute(Object object) { + //-------------------------------------- + // Marker was updated + //-------------------------------------- + decreaseWaitCnt(clusterId); + synchronized (pluginMarkers) { + //Log.d(TAG, "create : " + fMarkerId + " = CREATED"); + pluginMarkers.put(fMarkerId, STATUS.CREATED); + } + } + + @Override + public void onError(String errorMsg) { + //-------------------------------------- + // Could not read icon for some reason + //-------------------------------------- + Log.e(TAG, errorMsg); + decreaseWaitCnt(clusterId); + synchronized (deleteMarkers) { + deleteMarkers.add(fMarkerId); + } + synchronized (pluginMarkers) { + pluginMarkers.put(fMarkerId, STATUS.DELETED); + } + } + }); + } else { + //-------------------- + // No icon for marker + //-------------------- + synchronized (pluginMarkers) { + //Log.d(TAG, "create : " + clusterId_markerId + " = CREATED"); + pluginMarkers.put(clusterId_markerId, STATUS.CREATED); + } + decreaseWaitCnt(clusterId); + } + } + updateClusterIDs.clear(); + + + } + }); + synchronized (semaphore) { + try { + semaphore.wait(); + deleteProcess(clusterId, params); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + callbackContext.success(allResults); + + } + private void deleteProcess(final String clusterId, final JSONObject params) { + if (!params.has("delete")) { + return; + } + cordova.getThreadPool().submit(new Runnable() { + @Override + public void run() { + + try { + JSONArray deleteClusters = params.getJSONArray("delete"); + if (deleteClusters != null) { + //------------------------------------- + // delete markers on the delete thread + //------------------------------------- + int deleteCnt = deleteClusters.length(); + String clusterId_markerId; + for (int i = 0; i < deleteCnt; i++) { + clusterId_markerId = clusterId + "-" + deleteClusters.getString(i); + deleteMarkers.add(clusterId_markerId); + } + + synchronized (deleteThreadLock) { + deleteThreadLock.notify(); + } + + } + } catch (Exception e) {e.printStackTrace();} + } + }); + } + + private void decreaseWaitCnt(String clusterId) { + + //-------------------------------------- + // Icon is set to marker + //-------------------------------------- + synchronized (waitCntManager) { + int waitCnt = waitCntManager.get(clusterId); + waitCnt = waitCnt - 1; + //Log.d(TAG, "--->waitCnt = " + waitCnt); + if (waitCnt == 0) { + synchronized (semaphore) { + semaphore.notify(); + } + + } + waitCntManager.put(clusterId, waitCnt); + } + } + + private void setIconToClusterMarker(final String markerId, final Marker marker, final Bundle iconProperty, final PluginAsyncInterface callback) { + synchronized (pluginMarkers) { + if (STATUS.DELETED.equals(pluginMarkers.get(markerId))) { + synchronized (pluginMap.objects) { + PluginMarkerCluster.this._removeMarker(marker); + pluginMap.objects.remove(markerId); + pluginMap.objects.remove("marker_property_" + markerId); + } + pluginMarkers.remove(markerId); + callback.onError("marker has been removed"); + return; + } + } + PluginMarkerCluster.super.setIcon_(marker, iconProperty, new PluginAsyncInterface() { + @Override + public void onPostExecute(Object object) { + Marker marker = (Marker) object; + synchronized (pluginMarkers) { + if (STATUS.DELETED.equals(pluginMarkers.get(markerId))) { + synchronized (pluginMap.objects) { + PluginMarkerCluster.this._removeMarker(marker); + pluginMap.objects.remove(markerId); + pluginMap.objects.remove("marker_property_" + markerId); + } + pluginMarkers.remove(markerId); + callback.onPostExecute(null); + return; + } + marker.setVisible(true); + + //Log.d(TAG, "create : " + markerId + " = CREATED"); + pluginMarkers.put(markerId, STATUS.CREATED); + callback.onPostExecute(object); + } + } + + @Override + public void onError(String errorMsg) { + synchronized (pluginMap.objects) { + if (marker != null && marker.getTag() != null) { + PluginMarkerCluster.this._removeMarker(marker); + } + pluginMap.objects.remove(markerId); + pluginMap.objects.remove("marker_property_" + markerId); + pluginMarkers.remove(markerId); + pluginMap.objects.remove(markerId); + pluginMarkers.put(markerId, STATUS.DELETED); + } + callback.onPostExecute(errorMsg); + } + }); + } + + + + // The maximum *practical* geocell resolution. + private static final int GEOCELL_GRID_SIZE = 4; + public static final String GEOCELL_ALPHABET = "0123456789abcdef"; + + private String getGeocell(double lat, double lng, int resolution) { + StringBuilder cell = new StringBuilder(); + double north = 90.0; + double south = -90.0; + double east = 180.0; + double west = -180.0; + double subcell_lng_span, subcell_lat_span; + byte x, y; + while(cell.length() < resolution + 1) { + subcell_lng_span = (east - west) / GEOCELL_GRID_SIZE; + subcell_lat_span = (north - south) / GEOCELL_GRID_SIZE; + + x = (byte)Math.min(Math.floor(GEOCELL_GRID_SIZE * (lng - west) / (east - west)), GEOCELL_GRID_SIZE - 1); + y = (byte)Math.min(Math.floor(GEOCELL_GRID_SIZE * (lat - south) / (north - south)), GEOCELL_GRID_SIZE - 1); + cell.append(_subdiv_char(x, y)); + + south += subcell_lat_span * y; + north = south + subcell_lat_span; + + west += subcell_lng_span * x; + east = west + subcell_lng_span; + } + return cell.toString(); + } + private char _subdiv_char(int posX, int posY) { + return GEOCELL_ALPHABET.charAt( + (posY & 2) << 2 | + (posX & 2) << 1 | + (posY & 1) << 1 | + (posX & 1) << 0); + } + + private double[] _subdiv_xy(char cellChar) { + int charI = GEOCELL_ALPHABET.indexOf(cellChar); + return new double[]{ + (double)((charI & 4) >> 1 | (charI & 1) >> 0) + 0.0f, + (double)((charI & 8) >> 2 | (charI & 2) >> 1) + 0.0f + }; + } + + private LatLngBounds computeBox(String geocell) { + + //String tmp[] = geocell.split("-"); + //geocell = tmp[1]; + + double subcell_lng_span, subcell_lat_span; + double x, y; + double xy[]; + BoundBox bbox = new BoundBox(90.0, 180.0, -90.0, -180.0); + + while (geocell.length() > 0) { + subcell_lng_span = (double)(bbox.getEast() - bbox.getWest()) / (double)GEOCELL_GRID_SIZE; + subcell_lat_span = (double)(bbox.getNorth() - bbox.getSouth()) / (double)GEOCELL_GRID_SIZE; + + xy = _subdiv_xy(geocell.charAt(0)); + x = xy[0]; + y = xy[1]; + + bbox = new BoundBox(bbox.getSouth() + subcell_lat_span * (y + 1.0f), + bbox.getWest() + subcell_lng_span * (double)(x + 1.0f), + bbox.getSouth() + subcell_lat_span * y, + bbox.getWest() + subcell_lng_span * x); + + geocell = geocell.substring(1); + } + LatLng sw = new LatLng(bbox.getSouth(), bbox.getWest()); + LatLng ne = new LatLng(bbox.getNorth(), bbox.getEast()); + return new LatLngBounds(sw, ne); + } + + private class BoundBox { + double north_, south_, east_, west_; + + BoundBox(double north, double east, double south, double west) { + if (south > north) { + south_ = north; + north_ = south; + } else { + south_ = south; + north_ = north; + } + west_ = west; + east_ = east; + } + + + public double getNorth() { + return north_; + } + + public double getSouth() { + return south_; + } + + public double getWest() { + return west_; + } + + public double getEast() { + return east_; + } + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginPolygon.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginPolygon.java new file mode 100644 index 0000000..231cd58 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginPolygon.java @@ -0,0 +1,753 @@ +package plugin.google.maps; + +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; +import com.google.android.gms.maps.model.Polygon; +import com.google.android.gms.maps.model.PolygonOptions; + +import org.apache.cordova.CallbackContext; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +//Future implement +//TODO: https://codepen.io/jhawes/pen/ujdgK + + +public class PluginPolygon extends MyPlugin implements MyPluginInterface { + + private String polygonHashCode; + + /** + * Create polygon + * @param args + * @param callbackContext + * @throws JSONException + */ + @Override + public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + self = this; + + final PolygonOptions polygonOptions = new PolygonOptions(); + int color; + final LatLngBounds.Builder builder = new LatLngBounds.Builder(); + final JSONObject properties = new JSONObject(); + final ArrayList holeBounds = new ArrayList(); + final ArrayList path = new ArrayList(); + final ArrayList> holePaths = new ArrayList>(); + + JSONObject opts = args.getJSONObject(1); + final String hashCode = args.getString(2); + polygonHashCode = hashCode; + if (opts.has("points")) { + JSONArray points = opts.getJSONArray("points"); + ArrayList path2 = PluginUtil.JSONArray2LatLngList(points); + for (int i = 0; i < path2.size(); i++) { + polygonOptions.add(path2.get(i)); + path.add(path2.get(i)); + builder.include(path2.get(i)); + } + } + + if (opts.has("holes")) { + JSONArray holes = opts.getJSONArray("holes"); + int i; + JSONArray latLngArray; + ArrayList hole; + Iterator iterator; + LatLng latLng; + LatLngBounds.Builder builder2; + for (i = 0; i < holes.length(); i++) { + latLngArray = holes.getJSONArray(i); + hole = PluginUtil.JSONArray2LatLngList(latLngArray); + polygonOptions.addHole(hole); + holePaths.add(hole); + iterator = hole.iterator(); + builder2 = new LatLngBounds.Builder(); + while (iterator.hasNext()) { + latLng = iterator.next(); + builder2.include(latLng); + } + holeBounds.add(builder2.build()); + } + } + if (opts.has("strokeColor")) { + color = PluginUtil.parsePluginColor(opts.getJSONArray("strokeColor")); + polygonOptions.strokeColor(color); + } + if (opts.has("fillColor")) { + color = PluginUtil.parsePluginColor(opts.getJSONArray("fillColor")); + polygonOptions.fillColor(color); + } + if (opts.has("strokeWidth")) { + polygonOptions.strokeWidth((float)(opts.getDouble("strokeWidth") * density)); + } + if (opts.has("visible")) { + polygonOptions.visible(opts.getBoolean("visible")); + } + if (opts.has("geodesic")) { + polygonOptions.geodesic(opts.getBoolean("geodesic")); + } + if (opts.has("zIndex")) { + polygonOptions.zIndex(opts.getInt("zIndex")); + } + if (opts.has("clickable")) { + properties.put("isClickable", opts.getBoolean("clickable")); + } else { + properties.put("isClickable", true); + } + properties.put("isVisible", polygonOptions.isVisible()); + properties.put("zIndex", polygonOptions.getZIndex()); + properties.put("isGeodesic", polygonOptions.isGeodesic()); + + // Since this plugin uses own detecting process, + // set false to the clickable property. + polygonOptions.clickable(false); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Polygon polygon = map.addPolygon(polygonOptions); + String id = hashCode; + polygon.setTag(hashCode); + pluginMap.objects.put("polygon_"+ id, polygon); + pluginMap.objects.put("polygon_bounds_" + id, builder.build()); + pluginMap.objects.put("polygon_path_" + id, path); + pluginMap.objects.put("polygon_holePaths_" + id, holePaths); + pluginMap.objects.put("polygon_property_" + id, properties); + + JSONObject result = new JSONObject(); + try { + result.put("hashCode", hashCode); + result.put("__pgmId", "polygon_"+ id); + } catch (JSONException e) { + e.printStackTrace(); + } + callbackContext.success(result); + } + }); + } + + + @Override + public void onDestroy() { + super.onDestroy(); + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Set keySet = pluginMap.objects.keys; + if (keySet.size() > 0) { + String[] objectIdArray = keySet.toArray(new String[keySet.size()]); + + for (String objectId : objectIdArray) { + if (pluginMap.objects.containsKey(objectId)) { + if (objectId.contains("property")) { + Polygon polygon = (Polygon) pluginMap.objects.remove(objectId.replace("property_", "")); + if (polygon != null) { + polygon.remove(); + } + } + Object object = pluginMap.objects.remove(objectId); + object = null; + + } + } + } + } + }); + + } + + /** + * set fill color + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setFillColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + int color = PluginUtil.parsePluginColor(args.getJSONArray(1)); + this.setInt("setFillColor", id, color, callbackContext); + } + + /** + * set stroke color + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setStrokeColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + int color = PluginUtil.parsePluginColor(args.getJSONArray(1)); + this.setInt("setStrokeColor", id, color, callbackContext); + } + + /** + * set stroke width + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setStrokeWidth(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + float width = (float)(args.getDouble(1) * density); + this.setFloat("setStrokeWidth", id, width, callbackContext); + } + + /** + * set z-index + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final float zIndex = (float) args.getDouble(1); + this.setFloat("setZIndex", id, zIndex, callbackContext); + } + + /** + * set geodesic + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setGeodesic(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + boolean isGeodisic = args.getBoolean(1); + this.setBoolean("setGeodesic", id, isGeodisic, callbackContext); + } + + /** + * Remove the polygon + * @param args + * @param callbackContext + * @throws JSONException + */ + public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final Polygon polygon = this.getPolygon(id); + if (polygon == null) { + callbackContext.success(); + return; + } + pluginMap.objects.remove(id); + + pluginMap.objects.remove("polygon_bounds_" + polygonHashCode); + pluginMap.objects.remove("polygon_property_" + polygonHashCode); + pluginMap.objects.remove("polygon_path_" + polygonHashCode); + pluginMap.objects.remove("polygon_holePaths_" + polygonHashCode); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + polygon.remove(); + callbackContext.success(); + } + }); + } + + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void removePointAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int index = args.getInt(1); + final Polygon polygon = this.getPolygon(id); + + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_path_" + polygonHashCode; + final ArrayList path = (ArrayList)pluginMap.objects.get(propertyId); + if (path.size() > 0) { + path.remove(index); + } + pluginMap.objects.put(propertyId, path); + + //----------------------------------- + // Recalculate the polygon bounds + //----------------------------------- + propertyId = "polygon_bounds_" + polygonHashCode; + if (path.size() > 0) { + pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path)); + } else { + pluginMap.objects.remove(propertyId); + } + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + if (path.size() > 0) { + try { + polygon.setPoints(path); + } catch (Exception e) { + // Ignore this error + //e.printStackTrace(); + } + } else { + polygon.setVisible(false); + } + callbackContext.success(); + } + }); + } + + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setPoints(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + String id = args.getString(0); + final JSONArray positionList = args.getJSONArray(1); + + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_path_" + polygonHashCode; + final ArrayList path = (ArrayList)pluginMap.objects.get(propertyId); + path.clear(); + JSONObject position; + for (int i = 0; i < positionList.length(); i++) { + position = positionList.getJSONObject(i); + path.add(new LatLng(position.getDouble("lat"), position.getDouble("lng"))); + } + pluginMap.objects.put(propertyId, path); + + //----------------------------------- + // Recalculate the polygon bounds + //----------------------------------- + propertyId = "polygon_bounds_" + polygonHashCode; + pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path)); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setPoints(path); + if (path.size() > 0) { + polygon.setVisible(polygon.isVisible()); + } else { + polygon.setVisible(false); + } + callbackContext.success(); + } + }); + } + + /** + * Insert a point + * @param args + * @param callbackContext + * @throws JSONException + */ + public void insertPointAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int index = args.getInt(1); + JSONObject position = args.getJSONObject(2); + final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng")); + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + boolean shouldBeVisible = false; + String propertyId = "polygon_path_" + polygonHashCode; + final ArrayList path = (ArrayList)pluginMap.objects.get(propertyId); + if (path.size() == 0) { + JSONObject properties = (JSONObject)pluginMap.objects.get("polygon_property_" + polygonHashCode); + if (properties.getBoolean("isVisible")) { + shouldBeVisible = true; + } + } + path.add(index, latLng); + pluginMap.objects.put(propertyId, path); + + //----------------------------------- + // Recalculate the polygon bounds + //----------------------------------- + propertyId = "polygon_bounds_" + polygonHashCode; + pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path)); + + final boolean changeToVisible = shouldBeVisible; + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setPoints(path); + if (changeToVisible) { + polygon.setVisible(true); + } + callbackContext.success(); + } + }); + } + + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setPointAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int index = args.getInt(1); + JSONObject position = args.getJSONObject(2); + final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng")); + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_path_" + polygonHashCode; + final ArrayList path = (ArrayList)pluginMap.objects.get(propertyId); + path.set(index, latLng); + pluginMap.objects.put(propertyId, path); + + //----------------------------------- + // Recalculate the polygon bounds + //----------------------------------- + propertyId = "polygon_bounds_" + polygonHashCode; + pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path)); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setPoints(path); + callbackContext.success(); + } + }); + } + + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setHoles(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final JSONArray holeList = args.getJSONArray(1); + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_holePaths_" + polygonHashCode; + final ArrayList> holes = (ArrayList>) pluginMap.objects.get(propertyId); + for (int i = 0; i < holes.size(); i++) { + holes.get(i).clear(); + } + holes.clear(); + + JSONObject position; + for (int i = 0; i < holeList.length(); i++) { + ArrayList hole = new ArrayList(); + JSONArray holePositions = holeList.getJSONArray(i); + for (int j = 0; j < holePositions.length(); j++) { + position = holePositions.getJSONObject(j); + hole.add(new LatLng(position.getDouble("lat"), position.getDouble("lng"))); + } + holes.add(hole); + } + + + pluginMap.objects.put(propertyId, holes); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setHoles(holes); + callbackContext.success(); + } + }); + } + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void insertPointOfHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int holeIndex = args.getInt(1); + final int pointIndex = args.getInt(2); + JSONObject position = args.getJSONObject(3); + final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng")); + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_holePaths_" + polygonHashCode; + final ArrayList> holes = (ArrayList>) pluginMap.objects.get(propertyId); + ArrayList hole = null; + if (holeIndex < holes.size()) { + hole = holes.get(holeIndex); + } + if (hole == null) { + hole = new ArrayList(); + } + if (holes.size() == 0) { + holes.add(hole); + } + hole.add(pointIndex, latLng); + pluginMap.objects.put(propertyId, holes); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setHoles(holes); + callbackContext.success(); + } + }); + } + + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setPointOfHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int holeIndex = args.getInt(1); + final int pointIndex = args.getInt(2); + JSONObject position = args.getJSONObject(3); + final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng")); + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_holePaths_" + polygonHashCode; + final ArrayList> holes = (ArrayList>) pluginMap.objects.get(propertyId); + ArrayList hole = null; + if (holeIndex < holes.size()) { + hole = holes.get(holeIndex); + } + if (hole == null) { + hole = new ArrayList(); + } + if (holes.size() == 0) { + holes.add(hole); + } + hole.set(pointIndex, latLng); + pluginMap.objects.put(propertyId, holes); + + final ArrayList newHole = hole; + + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setHoles(holes); + callbackContext.success(); + } + }); + } + + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void removePointOfHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int holeIndex = args.getInt(1); + final int pointIndex = args.getInt(2); + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_holePaths_" + polygonHashCode; + final ArrayList> holes = (ArrayList>) pluginMap.objects.get(propertyId); + ArrayList hole = null; + if (holeIndex < holes.size()) { + hole = holes.get(holeIndex); + } + if (hole == null) { + hole = new ArrayList(); + } + if (holes.size() == 0) { + holes.add(hole); + } + hole.remove(pointIndex); + pluginMap.objects.put(propertyId, holes); + + final ArrayList newHole = hole; + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setHoles(holes); + callbackContext.success(); + } + }); + } + + + public void insertHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int holeIndex = args.getInt(1); + + JSONArray holeJson = args.getJSONArray(2); + final ArrayList newHole = PluginUtil.JSONArray2LatLngList(holeJson); + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_holePaths_" + polygonHashCode; + final ArrayList> holes = (ArrayList>) pluginMap.objects.get(propertyId); + holes.add(holeIndex, newHole); + pluginMap.objects.put(propertyId, holes); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + try { + polygon.setHoles(holes); + } catch (Exception e) { + // Ignore this error + //e.printStackTrace(); + } + callbackContext.success(); + } + }); + } + + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int holeIndex = args.getInt(1); + + JSONArray holeJson = args.getJSONArray(2); + final ArrayList newHole = PluginUtil.JSONArray2LatLngList(holeJson); + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_holePaths_" + polygonHashCode; + final ArrayList> holes = (ArrayList>) pluginMap.objects.get(propertyId); + holes.set(holeIndex, newHole); + pluginMap.objects.put(propertyId, holes); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setHoles(holes); + callbackContext.success(); + } + }); + } + + + /** + * Set points + * @param args + * @param callbackContext + * @throws JSONException + */ + public void removeHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final int holeIndex = args.getInt(1); + + final Polygon polygon = this.getPolygon(id); + + //------------------------ + // Update the hole list + //------------------------ + String propertyId = "polygon_holePaths_" + polygonHashCode; + final ArrayList> holes = (ArrayList>) pluginMap.objects.get(propertyId); + holes.remove(holeIndex); + pluginMap.objects.put(propertyId, holes); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + // Update the polygon + polygon.setHoles(holes); + callbackContext.success(); + } + }); + } + /** + * Set visibility for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException { + final boolean isVisible = args.getBoolean(1); + String id = args.getString(0); + + final Polygon polygon = this.getPolygon(id); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + polygon.setVisible(isVisible); + } + }); + String propertyId = "polygon_property_" + polygonHashCode; + JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId); + properties.put("isVisible", isVisible); + pluginMap.objects.put(propertyId, properties); + callbackContext.success(); + } + + /** + * Set clickable for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final boolean clickable = args.getBoolean(1); + String propertyId = id.replace("polygon_", "polygon_property_"); + JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId); + properties.put("isClickable", clickable); + pluginMap.objects.put(propertyId, properties); + callbackContext.success(); + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginPolyline.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginPolyline.java new file mode 100644 index 0000000..8d1f52c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginPolyline.java @@ -0,0 +1,334 @@ +package plugin.google.maps; + +import android.graphics.Color; +import android.util.Log; + +import com.google.android.gms.maps.model.CircleOptions; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; +import com.google.android.gms.maps.model.MarkerOptions; +import com.google.android.gms.maps.model.Polyline; +import com.google.android.gms.maps.model.PolylineOptions; + +import org.apache.cordova.CallbackContext; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class PluginPolyline extends MyPlugin implements MyPluginInterface { + private String polylineHashCode; + + /** + * Create polyline + * @param args + * @param callbackContext + * @throws JSONException + */ + public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + self = this; + + final PolylineOptions polylineOptions = new PolylineOptions(); + int color; + final LatLngBounds.Builder builder = new LatLngBounds.Builder(); + final JSONObject properties = new JSONObject(); + + JSONObject opts = args.getJSONObject(1); + final String hashCode = args.getString(2); + polylineHashCode = hashCode; + + if (opts.has("points")) { + JSONArray points = opts.getJSONArray("points"); + List path = PluginUtil.JSONArray2LatLngList(points); + int i = 0; + for (i = 0; i < path.size(); i++) { + polylineOptions.add(path.get(i)); + builder.include(path.get(i)); + } + } + if (opts.has("color")) { + color = PluginUtil.parsePluginColor(opts.getJSONArray("color")); + polylineOptions.color(color); + } + if (opts.has("width")) { + polylineOptions.width((float)(opts.getDouble("width") * density)); + } + if (opts.has("visible")) { + polylineOptions.visible(opts.getBoolean("visible")); + } + if (opts.has("geodesic")) { + polylineOptions.geodesic(opts.getBoolean("geodesic")); + } + if (opts.has("zIndex")) { + polylineOptions.zIndex(opts.getInt("zIndex")); + } + if (opts.has("clickable")) { + properties.put("isClickable", opts.getBoolean("clickable")); + } else { + properties.put("isClickable", true); + } + properties.put("isVisible", polylineOptions.isVisible()); + + // Since this plugin provide own click detection, + // disable default clickable feature. + polylineOptions.clickable(false); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + Polyline polyline = map.addPolyline(polylineOptions); + polyline.setTag(hashCode); + String id = "polyline_" + hashCode; + pluginMap.objects.put(id, polyline); + + String boundsId = "polyline_bounds_" + hashCode; + pluginMap.objects.put(boundsId, builder.build()); + + String propertyId = "polyline_property_" + hashCode; + pluginMap.objects.put(propertyId, properties); + + try { + JSONObject result = new JSONObject(); + result.put("hashCode", hashCode); + result.put("__pgmId", id); + callbackContext.success(result); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error("" + e.getMessage()); + } + } + + }); + } + + + /** + * set color + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setStrokeColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + int color = PluginUtil.parsePluginColor(args.getJSONArray(1)); + this.setInt("setColor", id, color, callbackContext); + } + + /** + * set width + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setStrokeWidth(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + float width = (float)(args.getDouble(1) * density); + this.setFloat("setWidth", id, width, callbackContext); + } + + /** + * set z-index + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + float zIndex = (float) args.getDouble(1); + this.setFloat("setZIndex", id, zIndex, callbackContext); + } + + + /** + * Remove the polyline + * @param args + * @param callbackContext + * @throws JSONException + */ + public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final Polyline polyline = this.getPolyline(id); + if (polyline == null) { + callbackContext.success(); + return; + } + pluginMap.objects.remove(id); + + id = "polyline_bounds_" + polylineHashCode; + pluginMap.objects.remove(id); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + polyline.remove(); + callbackContext.success(); + } + }); + } + public void setPoints(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + String id = args.getString(0); + final JSONArray positionList = args.getJSONArray(1); + + final Polyline polyline = this.getPolyline(id); + // Recalculate the polygon bounds + final String propertyId = "polyline_bounds_" + polylineHashCode; + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + try { + List path = polyline.getPoints(); + path.clear(); + JSONObject position; + for (int i = 0; i < positionList.length(); i++) { + position = positionList.getJSONObject(i); + path.add(new LatLng(position.getDouble("lat"), position.getDouble("lng"))); + } + polyline.setPoints(path); + pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path)); + } catch (JSONException e) { + e.printStackTrace(); + } + callbackContext.success(); + } + }); + } + public void removePointAt(final JSONArray args, CallbackContext callbackContext) throws JSONException { + + String id = args.getString(0); + final int index = args.getInt(1); + final Polyline polyline = this.getPolyline(id); + + // Recalculate the polygon bounds + final String propertyId = "polyline_bounds_" + polylineHashCode; + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + List path = polyline.getPoints(); + if (path.size() > index) { + path.remove(index); + if (path.size() > 0) { + pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path)); + } else { + pluginMap.objects.remove(propertyId); + } + + polyline.setPoints(path); + } + } + }); + callbackContext.success(); + } + public void insertPointAt(final JSONArray args, CallbackContext callbackContext) throws JSONException { + + String id = args.getString(0); + final int index = args.getInt(1); + JSONObject position = args.getJSONObject(2); + final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng")); + + final Polyline polyline = this.getPolyline(id); + + + // Recalculate the polygon bounds + final String propertyId = "polyline_bounds_" + polylineHashCode; + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + List path = polyline.getPoints(); + if (path.size() >= index) { + path.add(index, latLng); + polyline.setPoints(path); + pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path)); + } + } + }); + callbackContext.success(); + } + public void setPointAt(final JSONArray args, CallbackContext callbackContext) throws JSONException { + + String id = args.getString(0); + final int index = args.getInt(1); + JSONObject position = args.getJSONObject(2); + final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng")); + + + final Polyline polyline = this.getPolyline(id); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + List path = polyline.getPoints(); + if (path.size() > index) { + path.set(index, latLng); + + // Recalculate the polygon bounds + String propertyId = "polyline_bounds_" + polylineHashCode; + pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path)); + + polyline.setPoints(path); + } + } + }); + callbackContext.success(); + } + + /** + * set geodesic + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setGeodesic(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + boolean isGeodisic = args.getBoolean(1); + this.setBoolean("setGeodesic", id, isGeodisic, callbackContext); + } + + /** + * Set visibility for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final boolean isVisible = args.getBoolean(1); + + final Polyline polyline = this.getPolyline(id); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + polyline.setVisible(isVisible); + } + }); + String propertyId = "polyline_property_" + polylineHashCode; + JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId); + properties.put("isVisible", isVisible); + pluginMap.objects.put(propertyId, properties); + callbackContext.success(); + } + + /** + * Set clickable for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + final boolean clickable = args.getBoolean(1); + String propertyId = id.replace("polyline_", "polyline_property_"); + JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId); + properties.put("isClickable", clickable); + pluginMap.objects.put(propertyId, properties); + callbackContext.success(); + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginStreetViewPanorama.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginStreetViewPanorama.java new file mode 100644 index 0000000..b85a51e --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginStreetViewPanorama.java @@ -0,0 +1,459 @@ +package plugin.google.maps; + +import android.app.Activity; +import android.graphics.Point; +import android.view.View; +import android.view.ViewGroup; + +import com.google.android.gms.maps.OnStreetViewPanoramaReadyCallback; +import com.google.android.gms.maps.StreetViewPanorama; +import com.google.android.gms.maps.StreetViewPanoramaOptions; +import com.google.android.gms.maps.StreetViewPanoramaView; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.StreetViewPanoramaCamera; +import com.google.android.gms.maps.model.StreetViewPanoramaLink; +import com.google.android.gms.maps.model.StreetViewPanoramaLocation; +import com.google.android.gms.maps.model.StreetViewPanoramaOrientation; +import com.google.android.gms.maps.model.StreetViewSource; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaWebView; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Locale; + + +public class PluginStreetViewPanorama extends MyPlugin implements + IPluginView, StreetViewPanorama.OnStreetViewPanoramaCameraChangeListener, + StreetViewPanorama.OnStreetViewPanoramaChangeListener, + StreetViewPanorama.OnStreetViewPanoramaClickListener { + + private Activity mActivity; + private StreetViewPanoramaView panoramaView; + private StreetViewPanorama panorama; + private String panoramaId; + private boolean isVisible = true; + private boolean isClickable = true; + private final String TAG = "StreetView"; + private String divId; + private int viewDepth = 0; + + @Override + public void initialize(CordovaInterface cordova, final CordovaWebView webView) { + super.initialize(cordova, webView); + mActivity = cordova.getActivity(); + } + + public int getViewDepth() { + return viewDepth; + } + public String getDivId() { + return this.divId; + } + public String getOverlayId() { + return this.panoramaId; + } + public ViewGroup getView() { + return this.panoramaView; + } + + + public boolean getVisible() { + return isVisible; + } + public boolean getClickable() { + return isClickable; + } + public void getPanorama(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + JSONObject meta = args.getJSONObject(0); + panoramaId = meta.getString("__pgmId"); + viewDepth = meta.getInt("depth"); + + JSONObject jsOptions = args.getJSONObject(1); + divId = args.getString(2); + + StreetViewPanoramaOptions svOptions = new StreetViewPanoramaOptions(); + if (jsOptions.has("camera")) { + JSONObject cameraOpts = jsOptions.getJSONObject("camera"); + Object target = cameraOpts.get("target"); + if (target instanceof JSONObject) { + JSONObject targetJson = cameraOpts.getJSONObject("target"); + LatLng position = new LatLng(targetJson.getDouble("lat"), targetJson.getDouble("lng")); + + if (cameraOpts.has("source")) { + StreetViewSource source = "OUTDOOR".equals(cameraOpts.getString("source")) ? + StreetViewSource.OUTDOOR : StreetViewSource.DEFAULT; + if (cameraOpts.has("radius")) { + svOptions.position(position, cameraOpts.getInt("radius"), source); + } else { + svOptions.position(position, source); + } + } else { + if (cameraOpts.has("radius")) { + svOptions.position(position, cameraOpts.getInt("radius")); + } else { + svOptions.position(position); + } + } + } else if (target instanceof String) { + svOptions.panoramaId(cameraOpts.getString("target")); + } + + if (cameraOpts.has("bearing") || + cameraOpts.has("tilt") || + cameraOpts.has("zoom")) { + StreetViewPanoramaCamera.Builder builder = StreetViewPanoramaCamera.builder(); + if (cameraOpts.has("bearing")) { + builder.bearing = (float) cameraOpts.getDouble("bearing"); + } + if (cameraOpts.has("tilt")) { + builder.tilt = (float) cameraOpts.getDouble("tilt"); + } + if (cameraOpts.has("zoom")) { + builder.zoom = (float) cameraOpts.getDouble("zoom"); + } + svOptions.panoramaCamera(builder.build()); + } + } + + if (jsOptions.has("gestures")) { + JSONObject gestures = jsOptions.getJSONObject("gestures"); + if (gestures.has("panning")) { + svOptions.panningGesturesEnabled(gestures.getBoolean("panning")); + } + if (gestures.has("zoom")) { + svOptions.zoomGesturesEnabled(gestures.getBoolean("zoom")); + } + } + + if (jsOptions.has("controls")) { + JSONObject controls = jsOptions.getJSONObject("controls"); + if (controls.has("navigation")) { + svOptions.userNavigationEnabled(controls.getBoolean("navigation")); + } + if (controls.has("streetNames")) { + svOptions.streetNamesEnabled(controls.getBoolean("streetNames")); + } + } + panoramaView = new StreetViewPanoramaView(mActivity, svOptions); + + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + panoramaView.onCreate(null); + panoramaView.setTag(getViewDepth()); + + panoramaView.getStreetViewPanoramaAsync(new OnStreetViewPanoramaReadyCallback() { + @Override + public void onStreetViewPanoramaReady(StreetViewPanorama streetViewPanorama) { + panoramaView.onResume(); + panorama = streetViewPanorama; + + panorama.setOnStreetViewPanoramaCameraChangeListener(PluginStreetViewPanorama.this); + panorama.setOnStreetViewPanoramaChangeListener(PluginStreetViewPanorama.this); + panorama.setOnStreetViewPanoramaClickListener(PluginStreetViewPanorama.this); + + // Don't support this because iOS does not support this feature. + //panorama.setOnStreetViewPanoramaLongClickListener(PluginStreetViewPanorama.this); + + + mapCtrl.mPluginLayout.addPluginOverlay(PluginStreetViewPanorama.this); + callbackContext.success(); + } + }); + } + }); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public void onPause(boolean multitasking) { + super.onPause(multitasking); + if (panoramaView != null && panoramaView.isActivated()) { + panoramaView.onPause(); + } + + //mapCtrl.mPluginLayout.removePluginOverlay(this.panoramaId); + + } + @Override + public void onResume(boolean multitasking) { + super.onResume(multitasking); + if (panoramaView != null && panoramaView.isActivated()) { + panoramaView.onResume(); + } + //mapCtrl.mPluginLayout.addPluginOverlay(PluginStreetViewPanorama.this); + } + + public void attachToWebView(JSONArray args, final CallbackContext callbackContext) { + mapCtrl.mPluginLayout.addPluginOverlay(this); + callbackContext.success(); + } + + public void detachFromWebView(JSONArray args, final CallbackContext callbackContext) { + mapCtrl.mPluginLayout.removePluginOverlay(this.panoramaId); + callbackContext.success(); + } + + public void setPanningGesturesEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final boolean boolValue = args.getBoolean(0); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + panorama.setPanningGesturesEnabled(boolValue); + callbackContext.success(); + } + }); + } + + public void setZoomGesturesEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final boolean boolValue = args.getBoolean(0); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + panorama.setZoomGesturesEnabled(boolValue); + callbackContext.success(); + } + }); + } + + public void setNavigationEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final boolean boolValue = args.getBoolean(0); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + panorama.setUserNavigationEnabled(boolValue); + callbackContext.success(); + } + }); + } + + public void setStreetNamesEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final boolean boolValue = args.getBoolean(0); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + panorama.setStreetNamesEnabled(boolValue); + callbackContext.success(); + } + }); + } + public void setVisible(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + final boolean boolValue = args.getBoolean(0); + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + panoramaView.setVisibility(boolValue ? View.VISIBLE : View.INVISIBLE); + callbackContext.success(); + } + }); + } + + + public void setPosition(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + try { + JSONObject cameraOpts = args.getJSONObject(0); + Object target = cameraOpts.get("target"); + + if (target instanceof JSONObject) { + JSONObject targetJson = (JSONObject)target; + LatLng position = new LatLng(targetJson.getDouble("lat"), targetJson.getDouble("lng")); + + if (cameraOpts.has("source")) { + StreetViewSource source = "OUTDOOR".equals(cameraOpts.getString("source")) ? + StreetViewSource.OUTDOOR : StreetViewSource.DEFAULT; + if (cameraOpts.has("radius")) { + panorama.setPosition(position, cameraOpts.getInt("radius"), source); + } else { + panorama.setPosition(position, source); + } + } else { + if (cameraOpts.has("radius")) { + panorama.setPosition(position, cameraOpts.getInt("radius")); + } else { + panorama.setPosition(position); + } + } + } else if (target instanceof String) { + panorama.setPosition(cameraOpts.getString("target")); + } + + callbackContext.success(); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error("" + e.getMessage()); + } + } + }); + } + + public void setPov(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + try { + final JSONObject cameraPosition = args.getJSONObject(0); + + StreetViewPanoramaCamera currentCamera = panorama.getPanoramaCamera(); + float bearing = cameraPosition.has("bearing") ? (float) cameraPosition.getDouble("bearing") : currentCamera.bearing; + float tilt = cameraPosition.has("tilt") ? (float) cameraPosition.getDouble("tilt") : currentCamera.tilt; + float zoom = cameraPosition.has("zoom") ? (float) cameraPosition.getDouble("zoom") : currentCamera.zoom; + long duration = cameraPosition.has("duration") ? (long) cameraPosition.getDouble("duration") : 1000; + + StreetViewPanoramaCamera newCamera = new StreetViewPanoramaCamera.Builder() + .bearing(bearing) + .zoom(zoom) + .tilt(tilt) + .build(); + panorama.animateTo(newCamera, duration); + + callbackContext.success(); + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error("" + e.getMessage()); + } + } + }); + } + + public void remove(JSONArray args, final CallbackContext callbackContext) { + mapCtrl.mPluginLayout.removePluginOverlay(this.panoramaId); + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + panorama.setOnStreetViewPanoramaLongClickListener(null); + panorama.setOnStreetViewPanoramaClickListener(null); + panorama.setOnStreetViewPanoramaChangeListener(null); + panorama.setOnStreetViewPanoramaCameraChangeListener(null); + + System.gc(); + Runtime.getRuntime().gc(); + if (callbackContext != null) { + callbackContext.success(); + } + PluginStreetViewPanorama.this.onDestroy(); + } + }); + } + + @Override + public void onStreetViewPanoramaCameraChange(StreetViewPanoramaCamera streetViewPanoramaCamera) { + try { + JSONObject camera = new JSONObject(); + camera.put("bearing", streetViewPanoramaCamera.bearing); + camera.put("tilt", streetViewPanoramaCamera.tilt); + camera.put("zoom", streetViewPanoramaCamera.zoom); + +// StreetViewPanoramaOrientation svOrientation = streetViewPanoramaCamera.getOrientation(); +// JSONObject orientation = new JSONObject(); +// orientation.put("bearing", svOrientation.bearing); +// orientation.put("tilt", svOrientation.tilt); +// camera.put("orientation", orientation); + + String jsonStr = camera.toString(0); + jsCallback( + String.format( + Locale.ENGLISH, + "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'%s', callback:'_onPanoramaCameraChange', args: [%s]});}", + panoramaId, panoramaId, "panorama_camera_change", jsonStr)); + } catch (Exception e) { + // ignore + e.printStackTrace(); + } + + } + + private void jsCallback(final String js) { + this.mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + webView.loadUrl(js); + } + }); + } + + @Override + public void onStreetViewPanoramaChange(StreetViewPanoramaLocation streetViewPanoramaLocation) { + try { + String jsonStr = "null"; + if (streetViewPanoramaLocation != null) { + JSONObject location = new JSONObject(); + location.put("panoId", streetViewPanoramaLocation.panoId); + + JSONObject position = new JSONObject(); + position.put("lat", streetViewPanoramaLocation.position.latitude); + position.put("lng", streetViewPanoramaLocation.position.longitude); + location.put("latLng", position); + + JSONArray links = new JSONArray(); + for (StreetViewPanoramaLink stLink : streetViewPanoramaLocation.links) { + JSONObject link = new JSONObject(); + link.put("panoId", stLink.panoId); + link.put("bearing", stLink.bearing); + links.put(link); + } + location.put("links", links); + + jsonStr = location.toString(0); + } + jsCallback( + String.format( + Locale.ENGLISH, + "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'%s', callback:'_onPanoramaLocationChange', args: [%s]});}", + panoramaId, panoramaId, "panorama_location_change", jsonStr)); + } catch (Exception e) { + // ignore + e.printStackTrace(); + } + + } + + @Override + public void onStreetViewPanoramaClick(StreetViewPanoramaOrientation streetViewPanoramaOrientation) { + + try { + JSONObject clickInfo = new JSONObject(); + JSONObject orientation = new JSONObject(); + orientation.put("bearing", streetViewPanoramaOrientation.bearing); + orientation.put("tilt", streetViewPanoramaOrientation.tilt); + clickInfo.put("orientation", orientation); + + Point point = panorama.orientationToPoint(streetViewPanoramaOrientation); + JSONArray pointArray = new JSONArray(); + pointArray.put((int)((double)point.x / (double)density)); + pointArray.put((int)((double)point.y / (double)density)); + clickInfo.put("point", pointArray); + + String jsonStr = clickInfo.toString(0); + jsCallback( + String.format( + Locale.ENGLISH, + "javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'%s', callback:'_onPanoramaEvent', args: [%s]});}", + panoramaId, panoramaId, "panorama_click", jsonStr)); + } catch (Exception e) { + // ignore + e.printStackTrace(); + } + + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginTileOverlay.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginTileOverlay.java new file mode 100644 index 0000000..05ca806 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginTileOverlay.java @@ -0,0 +1,224 @@ +package plugin.google.maps; + +import android.content.res.AssetManager; +import android.view.View; +import android.webkit.WebView; + +import com.google.android.gms.maps.model.TileOverlay; +import com.google.android.gms.maps.model.TileOverlayOptions; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPreferences; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class PluginTileOverlay extends MyPlugin implements MyPluginInterface { + + + /** + * Create tile overlay + * + * @param args + * @param callbackContext + * @throws JSONException + */ + public void create(final JSONArray args, + final CallbackContext callbackContext) throws JSONException { + + + final JSONObject opts = args.getJSONObject(1); + final String id = args.getString(2); + final int tileSize = opts.getInt("tileSize"); + + + final TileOverlayOptions options = new TileOverlayOptions(); + if (opts.has("zIndex")) { + options.zIndex((float)opts.getDouble("zIndex")); + } + if (opts.has("visible")) { + options.visible(opts.getBoolean("visible")); + } + if (opts.has("opacity")) { + options.transparency((float)(1 - opts.getDouble("opacity"))); + } + + boolean isDebug = false; + if (opts.has("debug")) { + isDebug = opts.getBoolean("debug"); + } + final boolean _isDebug = isDebug; + + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + String userAgent = ""; + if (opts.has("userAgent")) { + try { + userAgent = opts.getString("userAgent"); + } catch (JSONException e) { + //e.printStackTrace(); + } + } + if ("".equals(userAgent)) { + View browserView = webView.getView(); + String browserViewName = browserView.getClass().getName(); + if ("org.xwalk.core.XWalkView".equals(browserViewName) || + "org.crosswalk.engine.XWalkCordovaView".equals(browserViewName)) { + + CordovaPreferences preferences = webView.getPreferences(); + // Set xwalk webview settings by Cordova preferences. + String xwalkUserAgent = preferences == null ? "" : preferences.getString("xwalkUserAgent", ""); + if (!xwalkUserAgent.isEmpty()) { + userAgent = xwalkUserAgent; + } + + String appendUserAgent = preferences.getString("AppendUserAgent", ""); + if (!appendUserAgent.isEmpty()) { + userAgent = userAgent + " " + appendUserAgent; + } + } else { + userAgent = ((WebView) webView.getEngine().getView()).getSettings().getUserAgentString(); + } + } + + String currentPageUrl = webView.getUrl(); + + AssetManager assetManager = cordova.getActivity().getAssets(); + final PluginTileProvider tileProvider = new PluginTileProvider(pluginMap.getOverlayId(), id, webView, assetManager, currentPageUrl, userAgent, tileSize, _isDebug); + tileProvider.setOnCacheClear(new PluginTileProvider.OnCacheClear() { + @Override + public void onCacheClear(int hashCode) { + TileOverlay tileOverlay = (TileOverlay)PluginTileOverlay.this.getTileOverlay(hashCode+""); + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + System.gc(); + } + } + }); + options.tileProvider(tileProvider); + + + TileOverlay tileOverlay = map.addTileOverlay(options); + //String id = tileOverlay.getId(); + + pluginMap.objects.put("tileoverlay_" + id, tileOverlay); + pluginMap.objects.put("tileprovider_" + id, tileProvider); + + try { + JSONObject result = new JSONObject(); + result.put("hashCode", id); + result.put("__pgmId", "tileoverlay_" + id); + callbackContext.success(result); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error("" + e.getMessage()); + } + } + }); + } + + @SuppressWarnings("unused") + public void onGetTileUrlFromJS(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + String urlKey = args.getString(1); + String tileUrl = args.getString(2); + String pluginId = "tileprovider_" + id; + if (pluginMap.objects.containsKey(pluginId)) { + ((PluginTileProvider)(this.pluginMap.objects.get(pluginId))).onGetTileUrlFromJS(urlKey, tileUrl); + } + callbackContext.success(); + } + + /** + * set z-index + * @param args + * @param callbackContext + * @throws JSONException + */ + @SuppressWarnings("unused") + public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + float zIndex = (float) args.getDouble(1); + this.setFloat("setZIndex", id, zIndex, callbackContext); + } + + /** + * Set visibility for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException { + boolean visible = args.getBoolean(1); + String id = args.getString(0); + this.setBoolean("setVisible", id, visible, callbackContext); + } + /** + * Remove this tile layer + * @param args + * @param callbackContext + * @throws JSONException + */ + public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException { + String id = args.getString(0); + TileOverlay tileOverlay = (TileOverlay)pluginMap.objects.get(id); + if (tileOverlay == null) { + callbackContext.success(); + return; + } + cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + try { + String id = args.getString(0); + TileOverlay tileOverlay = (TileOverlay)pluginMap.objects.remove(id); + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + tileOverlay.remove(); + tileOverlay = null; + } + id = id.replace("tileoverlay_", "tileprovider_"); + + if (pluginMap.objects.containsKey(id)) { + ((PluginTileProvider)(pluginMap.objects.get(id))).remove(); + } + //pluginMap.objects.put(id, null); + PluginTileProvider provider = (PluginTileProvider) pluginMap.objects.remove(id); + provider = null; + callbackContext.success(); + } catch (JSONException e) { + e.printStackTrace(); + callbackContext.error("" + e.getMessage()); + + } + } + }); + } + + /** + * Set fadeIn for the object + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setFadeIn(JSONArray args, CallbackContext callbackContext) throws JSONException { + boolean visible = args.getBoolean(1); + String id = args.getString(0); + this.setBoolean("setFadeIn", id, visible, callbackContext); + } + /** + * Set opacity for the tile layer + * @param args + * @param callbackContext + * @throws JSONException + */ + public void setOpacity(JSONArray args, CallbackContext callbackContext) throws JSONException { + double opacity = 1 - args.getDouble(1); + String id = args.getString(0); + this.setFloat("setTransparency", id, (float)opacity, callbackContext); + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginTileProvider.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginTileProvider.java new file mode 100644 index 0000000..1bbb3b8 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginTileProvider.java @@ -0,0 +1,440 @@ +package plugin.google.maps; + +import android.annotation.SuppressLint; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.os.Handler; +import android.os.Looper; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.Log; + +import com.google.android.gms.maps.model.Tile; +import com.google.android.gms.maps.model.TileProvider; + +import org.apache.cordova.CordovaWebView; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; + +public class PluginTileProvider implements TileProvider { + private final String TAG = "TileProvider"; + private int tileSize = 512; + private Paint tilePaint = new Paint(Paint.FILTER_BITMAP_FLAG); + private Paint debugPaint = null; + private TextPaint debugTextPaint = null; + private String userAgent = null; + private static BitmapCache tileCache = null; + private OnCacheClear listener = null; + private String webPageUrl = null; + private AssetManager assetManager; + private CordovaWebView webView; + private String mapId, pluginId; + private final HashMap tileUrlMap = new HashMap(); + private boolean isDebug = false; + private Handler handler; + private final Object semaphore = new Object(); + private Bitmap emptyBitmap = null; + private final HashSet cacheKeys = new HashSet(); + private boolean isRemoved = false; + + @SuppressLint({"NewApi", "JavascriptInterface"}) + public PluginTileProvider(String mapId, String pluginId, CordovaWebView webView, AssetManager assetManager, String webPageUrl, String userAgent, int tileSize, boolean isDebug) { + this.tileSize = tileSize; + //this.tilePaint.setAlpha((int) (opacity * 255)); + this.userAgent = userAgent == null ? "Mozilla" : userAgent; + this.webPageUrl = webPageUrl; + this.assetManager = assetManager; + this.webView = webView; + this.mapId = mapId; + this.pluginId = pluginId; + + // Get max available VM memory, exceeding this amount will throw an + // OutOfMemory exception. Stored in kilobytes as LruCache takes an + // int in its constructor. + int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); + + // Use 1/8th of the available memory for this memory cache. + int cacheSize = maxMemory / 8; + + tileCache = new BitmapCache(cacheSize); + + this.isDebug = isDebug; + if (isDebug) { + debugPaint = new Paint(); + debugPaint.setTextSize(20); + debugPaint.setColor(Color.RED); + debugPaint.setStrokeWidth(1); + debugPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + + debugTextPaint = new TextPaint(); + debugTextPaint.setTextSize(20); + debugTextPaint.setColor(Color.RED); + debugTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + } + + handler = new Handler(Looper.getMainLooper()); + + } + + public interface OnCacheClear { + public void onCacheClear(int hashCode); + } + + + public void onGetTileUrlFromJS(String urlKey, String tileUrl) { + synchronized (tileUrlMap) { + tileUrlMap.put(urlKey, tileUrl); + } + synchronized (semaphore) { + semaphore.notify(); + } + } + + public void remove() { + isRemoved = true; + synchronized (cacheKeys) { + Iterator iterator = cacheKeys.iterator(); + String cacheKey; + Bitmap image; + while(iterator.hasNext()) { + cacheKey = iterator.next(); + image = tileCache.remove(cacheKey); + if (image != null && !image.isRecycled()) { + image.recycle(); + image = null; + } + } + } + tileCache.evictAll(); + + } + public void setOnCacheClear(OnCacheClear listener) { + this.listener = listener; + } + + @Override + public Tile getTile(int x, int y, int zoom) { + if (isRemoved) { + return null; + } + + String urlStr = null; + String originalUrlStr = null; + final String urlKey = String.format(Locale.US, "%s-%s-%d-%d-%d", mapId, pluginId, x, y, zoom); + synchronized (semaphore) { + final String js = String.format(Locale.ENGLISH, "javascript:if(window.cordova){cordova.fireDocumentEvent('%s-%s-tileoverlay', {key: \"%s\", x: %d, y: %d, zoom: %d});}", + mapId, pluginId, urlKey, x, y, zoom); + + handler.post(new Runnable() { + @Override + public void run() { + webView.loadUrl(js); + } + }); + try { + semaphore.wait(10000); // Maximum wait 10sec + } catch (InterruptedException e) { + e.printStackTrace(); + return null; + } + } + synchronized (tileUrlMap) { + urlStr = tileUrlMap.remove(urlKey); + } + originalUrlStr = urlStr; + + Tile tile = null; + if (urlStr == null || "(null)".equals(urlStr)) { + if (isDebug) { + if (emptyBitmap == null) { + emptyBitmap = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888); + } + Bitmap dummyBitmap = emptyBitmap.copy(Config.ARGB_8888, true); + drawDebugInfo(dummyBitmap, x, y, zoom, originalUrlStr); + tile = new Tile(tileSize, tileSize, bitmapToByteArray(dummyBitmap)); + dummyBitmap.recycle(); + return tile; + } else { + return null; + } + } + + if (urlStr.indexOf("data:image/") == 0 && urlStr.contains(";base64,")) { + String[] tmp = urlStr.split(","); + Bitmap image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]); + tile = new Tile(tileSize, tileSize, bitmapToByteArray(image)); + image.recycle(); + image = null; + return tile; + } + + if (urlStr.startsWith("http://localhost") || + urlStr.startsWith("http://127.0.0.1")) { + if (urlStr.contains("://")) { + urlStr = urlStr.replaceAll("http://.+?/", "file:///android_asset/www/"); + } else { + // Avoid WebViewLocalServer (because can not make a connection for some reason) + urlStr = "file:///android_asset/www/".concat(urlStr); + } + } + + try { + InputStream inputStream = null; + if (urlStr.startsWith("http://") || urlStr.startsWith("https://")) { + //------------------------------- + // load image from the internet + //------------------------------- + + boolean redirect = true; + URL url = new URL(urlStr); + String cacheKey = url.hashCode() + ""; + Bitmap cachedImage = tileCache.get(cacheKey); + if (cachedImage != null && !cachedImage.isRecycled()) { + if (isDebug) { + Bitmap copyImage = cachedImage.copy(Config.ARGB_8888, true); + drawDebugInfo(copyImage, x, y, zoom, originalUrlStr); + tile = new Tile(tileSize, tileSize, bitmapToByteArray(copyImage)); + copyImage.recycle(); + } else { + tile = new Tile(tileSize, tileSize, bitmapToByteArray(cachedImage)); + } + //cachedImage.recycle(); // Don't recycle it. Need to keep the bitmap instance + return tile; + } + + HttpURLConnection http = null; + String cookies = null; + int redirectCnt = 0; + while(redirect && redirectCnt < 10) { + redirect = false; + http = (HttpURLConnection)url.openConnection(); + http.setRequestMethod("GET"); + http.setReadTimeout(3000); + http.setConnectTimeout(3000); + if (cookies != null) { + http.setRequestProperty("Cookie", cookies); + } + http.addRequestProperty("Accept-Language", "en-US,en;q=0.8"); + http.addRequestProperty("User-Agent", userAgent); + http.setInstanceFollowRedirects(true); + HttpURLConnection.setFollowRedirects(true); + + // normally, 3xx is redirect + int status = http.getResponseCode(); + if (status != HttpURLConnection.HTTP_OK) { + if (status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_SEE_OTHER) + redirect = true; + } + if (redirect) { + // get redirect url from "location" header field + url = new URL(http.getHeaderField("Location")); + + // get the cookie if need, for login + cookies = http.getHeaderField("Set-Cookie"); + + // Disconnect the current connection + http.disconnect(); + redirectCnt++; + } + } + if (http != null) { + inputStream = http.getInputStream(); + + Bitmap image = BitmapFactory.decodeStream(inputStream); + if (image != null) { + if (image.getWidth() != tileSize || image.getHeight() != tileSize) { + Bitmap tileImage = this.resizeForTile(image); + tileCache.put(cacheKey, tileImage.copy(Config.ARGB_8888, true)); + if (isDebug) { + drawDebugInfo(tileImage, x, y, zoom, originalUrlStr); + } + tile = new Tile(tileSize, tileSize, bitmapToByteArray(tileImage)); + tileImage.recycle(); + } else { + tileCache.put(cacheKey, image.copy(Config.ARGB_8888, true)); + + if (isDebug) { + drawDebugInfo(image, x, y, zoom, originalUrlStr); + } + tile = new Tile(tileSize, tileSize, bitmapToByteArray(image)); + } + cacheKeys.add(cacheKey); + image.recycle(); + } + http.disconnect(); + } + inputStream.close(); + } else { + //--------------------------------- + // load image from the local path + //--------------------------------- + + if (!urlStr.contains("://") && + !urlStr.startsWith("/") && + !urlStr.startsWith("www/") && + !urlStr.startsWith("./") && + !urlStr.startsWith("../")) { + urlStr = "./" + urlStr; + } + if (urlStr.startsWith("./") || urlStr.startsWith("../")) { + urlStr = urlStr.replace("././", "./"); + String currentPage = webPageUrl; + currentPage = currentPage.replaceAll("#.*$", ""); + currentPage = currentPage.replaceAll("\\?.*$", ""); + currentPage = currentPage.replaceAll("[^\\/]*$", ""); + urlStr = currentPage + "/" + urlStr; + } + String cacheKey = new File(urlStr).hashCode() + ""; + Bitmap cachedImage = tileCache.get(cacheKey); + if (cachedImage != null && !cachedImage.isRecycled()) { + if (isDebug) { + Bitmap copyImage = cachedImage.copy(Config.ARGB_8888, true); + drawDebugInfo(copyImage, x, y, zoom, originalUrlStr); + cachedImage.recycle(); + tile = new Tile(tileSize, tileSize, bitmapToByteArray(copyImage)); + copyImage.recycle(); + } else { + tile = new Tile(tileSize, tileSize, bitmapToByteArray(cachedImage)); + } + //cachedImage.recycle(); // Don't recycle it. Need to keep the bitmap instance + return tile; + } + + Bitmap image = null; + if (urlStr.indexOf("file://") == 0 && + !urlStr.contains("file:///android_asset/")) { + urlStr = urlStr.replace("file://", ""); + File tmp = new File(urlStr); + if (tmp.exists()) { + image = BitmapFactory.decodeFile(urlStr); + } else { + //Log.w("PluginTileProvider", "image is not found (" + urlStr + ")"); + return null; + } + } else { + //Log.d(TAG, "imgUrl = " + imgUrl); + if (urlStr.indexOf("file:///android_asset/") == 0) { + urlStr = urlStr.replace("file:///android_asset/", ""); + } + if (urlStr.contains("./")) { + try { + boolean isAbsolutePath = urlStr.startsWith("/"); + File relativePath = new File(urlStr); + urlStr = relativePath.getCanonicalPath(); + //Log.d(TAG, "imgUrl = " + imgUrl); + if (!isAbsolutePath) { + urlStr = urlStr.substring(1); + } + //Log.d(TAG, "imgUrl = " + imgUrl); + } catch (Exception e) { + //e.printStackTrace(); + } + } + //Log.d("PluginTileProvider", "urlStr = " + urlStr); + try { + inputStream = assetManager.open(urlStr); + image = BitmapFactory.decodeStream(inputStream); + Bitmap tmpImage = image.copy(Config.ARGB_8888, true); + image.recycle(); + image = tmpImage; + } catch (IOException e) { + //e.printStackTrace(); + return null; + } + } + if (image != null) { + if (image.getWidth() != tileSize || image.getHeight() != tileSize) { + Bitmap tileImage = this.resizeForTile(image); + tileCache.put(cacheKey, tileImage.copy(Config.ARGB_8888, true)); + if (isDebug) { + drawDebugInfo(tileImage, x, y, zoom, originalUrlStr); + } + tile = new Tile(tileSize, tileSize, bitmapToByteArray(tileImage)); + tileImage.recycle(); + } else { + tileCache.put(cacheKey, image.copy(Config.ARGB_8888, true)); + if (isDebug) { + drawDebugInfo(image, x, y, zoom, originalUrlStr); + } + tile = new Tile(tileSize, tileSize, bitmapToByteArray(image)); + } + image.recycle(); + cacheKeys.add(cacheKey); + } + + } + return tile; + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private byte[] bitmapToByteArray(Bitmap bitmap) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.WEBP, 99, outputStream); + return outputStream.toByteArray(); + } + + private void drawDebugInfo(Bitmap bitmap, int x, int y, int zoom, String url) { + Canvas canvas = new Canvas(bitmap); + canvas.drawLine(0, 0, tileSize, 0, debugPaint); + canvas.drawLine(0, 0, 0, tileSize, debugPaint); + canvas.drawText(String.format(Locale.US, "x = %d, y = %d, zoom = %d", x, y, zoom), 30, 30, debugPaint); + if (url != null) { + StaticLayout mTextLayout = new StaticLayout(url, debugTextPaint, + tileSize * 4 / 5, Layout.Alignment.ALIGN_NORMAL, + 1.0f, 0.0f, false); + canvas.save(); + canvas.translate(30, 60); + mTextLayout.draw(canvas); + canvas.restore(); + } + } + + private Bitmap resizeForTile(Bitmap bitmap) { + + if (bitmap == null) { + return null; + } + /** + * http://stackoverflow.com/questions/4821488/bad-image-quality-after-resizing-scaling-bitmap#7468636 + */ + Bitmap scaledBitmap = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888); + + float ratioX = tileSize / (float) bitmap.getWidth(); + float ratioY = tileSize / (float) bitmap.getHeight(); + float middleX = tileSize / 2.0f; + float middleY = tileSize / 2.0f; + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, tilePaint); + bitmap.recycle(); + + return scaledBitmap; + } + +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginUtil.java b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginUtil.java new file mode 100644 index 0000000..1f098ff --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/plugin/google/maps/PluginUtil.java @@ -0,0 +1,435 @@ +package plugin.google.maps; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.location.Location; +import android.net.Uri; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.Bundle; +import android.util.Base64; + +import com.google.android.gms.maps.model.IndoorBuilding; +import com.google.android.gms.maps.model.IndoorLevel; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; +import com.google.android.gms.maps.model.LatLngBounds.Builder; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaResourceApi; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class PluginUtil { + // Get resource id + // http://stackoverflow.com/a/37840674 + public static int getAppResource(Activity activity, String name, String type) { + return activity.getResources().getIdentifier(name, type, activity.getPackageName()); + } + + public static abstract class MyCallbackContext extends CallbackContext { + + public MyCallbackContext(String callbackId, CordovaWebView webView) { + super(callbackId, webView); + } + @Override + public void sendPluginResult(PluginResult pluginResult) { + this.onResult(pluginResult); + } + + abstract public void onResult(PluginResult pluginResult); + } + + public static boolean isNumeric(String str) + { + for (char c : str.toCharArray()) { + if (!Character.isDigit(c)) { + return false; + } + } + return true; + } + + public static LatLngBounds getBoundsFromCircle(LatLng center, double radius) { + double d2r = Math.PI / 180; // degrees to radians + double r2d = 180 / Math.PI; // radians to degrees + double earthsradius = 3963.189; // 3963 is the radius of the earth in miles + radius *= 0.000621371192; // convert to mile + + // find the raidus in lat/lon + double rlat = (radius / earthsradius) * r2d; + double rlng = rlat / Math.cos(center.latitude * d2r); + + LatLngBounds.Builder builder = new LatLngBounds.Builder(); + double ex, ey; + for (int i = 0; i < 360; i+=90) { + ey = center.longitude + (rlng * Math.cos(i * d2r)); // center a + radius x * cos(theta) + ex = center.latitude + (rlat * Math.sin(i * d2r)); // center b + radius y * sin(theta) + builder.include(new LatLng(ex, ey)); + } + return builder.build(); + } + + public static LatLngBounds getBoundsFromPath(List path) { + LatLngBounds.Builder builder = new LatLngBounds.Builder(); + + for (LatLng aPath : path) { + builder.include(aPath); + } + return builder.build(); + } + + public static String getAbsolutePathFromCDVFilePath(CordovaResourceApi resourceApi, String cdvFilePath) { + if (cdvFilePath.indexOf("cdvfile://") != 0) { + return null; + } + + //CordovaResourceApi resourceApi = webView.getResourceApi(); + Uri fileURL = resourceApi.remapUri(Uri.parse(cdvFilePath)); + File file = resourceApi.mapUriToFile(fileURL); + if (file == null) { + return null; + } + + try { + return file.getCanonicalPath(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static JSONObject location2Json(Location location) throws JSONException { + JSONObject latLng = new JSONObject(); + latLng.put("lat", location.getLatitude()); + latLng.put("lng", location.getLongitude()); + + JSONObject params = new JSONObject(); + params.put("latLng", latLng); + + if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + params.put("elapsedRealtimeNanos", location.getElapsedRealtimeNanos()); + } else { + params.put("elapsedRealtimeNanos", 0); + } + params.put("time", location.getTime()); + /* + SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); + Date date = new Date(location.getTime()); + params.put("timeFormatted", format.format(date)); + */ + if (location.hasAccuracy()) { + params.put("accuracy", location.getAccuracy()); + } + if (location.hasBearing()) { + params.put("bearing", location.getBearing()); + } + if (location.hasAltitude()) { + params.put("altitude", location.getAltitude()); + } + if (location.hasSpeed()) { + params.put("speed", location.getSpeed()); + } + params.put("provider", location.getProvider()); + params.put("hashCode", location.hashCode()); + return params; + } + + /** + * return color integer value + * @param arrayRGBA + * @throws JSONException + */ + public static int parsePluginColor(JSONArray arrayRGBA) throws JSONException { + return Color.argb(arrayRGBA.getInt(3), arrayRGBA.getInt(0), arrayRGBA.getInt(1), arrayRGBA.getInt(2)); + } + + public static ArrayList JSONArray2LatLngList(JSONArray points) throws JSONException { + ArrayList path = new ArrayList(); + JSONObject pointJSON; + int i = 0; + for (i = 0; i < points.length(); i++) { + pointJSON = points.getJSONObject(i); + path.add(new LatLng(pointJSON.getDouble("lat"), pointJSON.getDouble("lng"))); + } + return path; + } + /* + public static Set JSONArray2LatLngHash(JSONArray points) throws JSONException { + Set path = new HashSet(); + JSONObject pointJSON; + int i = 0; + for (i = 0; i < points.length(); i++) { + pointJSON = points.getJSONObject(i); + path.add(new LatLng(pointJSON.getDouble("lat"), pointJSON.getDouble("lng"))); + } + return path; + } + */ + public static LatLngBounds JSONArray2LatLngBounds(JSONArray points) throws JSONException { + List path = JSONArray2LatLngList(points); + Builder builder = LatLngBounds.builder(); + int i = 0; + for (i = 0; i < path.size(); i++) { + builder.include(path.get(i)); + } + return builder.build(); + } + + public static Bundle Json2Bundle(JSONObject json) { + Bundle mBundle = new Bundle(); + @SuppressWarnings("unchecked") + Iterator iter = json.keys(); + Object value; + while (iter.hasNext()) { + String key = iter.next(); + try { + value = json.get(key); + if (Boolean.class.isInstance(value)) { + mBundle.putBoolean(key, (Boolean)value); + } else if (Integer.class.isInstance(value)) { + mBundle.putInt(key, (Integer)value); + } else if (Long.class.isInstance(value)) { + mBundle.putLong(key, (Long)value); + } else if (Double.class.isInstance(value)) { + mBundle.putDouble(key, (Double)value); + } else if (JSONObject.class.isInstance(value)) { + mBundle.putBundle(key, Json2Bundle((JSONObject)value)); + } else if (JSONArray.class.isInstance(value)) { + JSONArray values = (JSONArray)value; + ArrayList strings = new ArrayList(); + for (int i = 0; i < values.length(); i++) { + strings.add(values.get(i) + ""); + } + mBundle.putStringArrayList(key, strings); + } else { + mBundle.putString(key, json.getString(key)); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + return mBundle; + } + + public static Bitmap resizeBitmap(Bitmap bitmap, int newWidth, int newHeight) { + if (bitmap == null) { + return null; + } + /** + * http://stackoverflow.com/questions/4821488/bad-image-quality-after-resizing-scaling-bitmap#7468636 + */ + Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888); + + float ratioX = newWidth / (float) bitmap.getWidth(); + float ratioY = newHeight / (float) bitmap.getHeight(); + float middleX = newWidth / 2.0f; + float middleY = newHeight / 2.0f; + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG)); + bitmap.recycle(); + + return scaledBitmap; + } + + public static Bitmap scaleBitmapForDevice(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + + float density = Resources.getSystem().getDisplayMetrics().density; + int newWidth = (int)(bitmap.getWidth() * density); + int newHeight = (int)(bitmap.getHeight() * density); + /* + Bitmap resizeBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true); + */ + /** + * http://stackoverflow.com/questions/4821488/bad-image-quality-after-resizing-scaling-bitmap#7468636 + */ + Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888); + + float ratioX = newWidth / (float) bitmap.getWidth(); + float ratioY = newHeight / (float) bitmap.getHeight(); + float middleX = newWidth / 2.0f; + float middleY = newHeight / 2.0f; + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG)); + bitmap.recycle(); + + return scaledBitmap; + } + + public static Bitmap getBitmapFromBase64encodedImage(String base64EncodedImage) { + byte[] byteArray= Base64.decode(base64EncodedImage, Base64.DEFAULT); + Bitmap image= null; + try { + image = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); + } catch (Exception e) { + e.printStackTrace(); + } + return image; + } + + + public static JSONObject Bundle2Json(Bundle bundle) { + JSONObject json = new JSONObject(); + Set keys = bundle.keySet(); + Iterator iter = keys.iterator(); + while (iter.hasNext()) { + String key = iter.next(); + try { + Object value = bundle.get(key); + if (Bundle.class.isInstance(value)) { + value = Bundle2Json((Bundle)value); + } + if (value.getClass().isArray()) { + JSONArray values = new JSONArray(); + Object[] objects = (Object[])value; + int i = 0; + for (i = 0; i < objects.length; i++) { + if (Bundle.class.isInstance(objects[i])) { + objects[i] = Bundle2Json((Bundle)objects[i]); + } + values.put(objects[i]); + } + json.put(key, values); + } else if (value.getClass() == ArrayList.class) { + JSONArray values = new JSONArray(); + Iterator listIterator = ((ArrayList)value).iterator(); + while(listIterator.hasNext()) { + value = listIterator.next(); + if (Bundle.class.isInstance(value)) { + value = Bundle2Json((Bundle)value); + } + values.put(value); + } + json.put(key, values); + } else { + json.put(key, value); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + return json; + } + + public static LatLngBounds convertToLatLngBounds(List points) { + LatLngBounds.Builder latLngBuilder = LatLngBounds.builder(); + Iterator iterator = points.listIterator(); + while (iterator.hasNext()) { + latLngBuilder.include(iterator.next()); + } + return latLngBuilder.build(); + } + + + public static JSONObject convertIndoorBuildingToJson(IndoorBuilding indoorBuilding) { + if (indoorBuilding == null) { + return null; + } + JSONObject result = new JSONObject(); + try { + JSONArray levels = new JSONArray(); + for(IndoorLevel level : indoorBuilding.getLevels()){ + JSONObject levelInfo = new JSONObject(); + levelInfo.put("name",level.getName()); + + // TODO Auto-generated catch block + levelInfo.put("shortName",level.getShortName()); + levels.put(levelInfo); + } + result.put("activeLevelIndex",indoorBuilding.getActiveLevelIndex()); + result.put("defaultLevelIndex",indoorBuilding.getDefaultLevelIndex()); + result.put("levels",levels); + result.put("underground",indoorBuilding.isUnderground()); + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + return result; + } + + public static ArrayList unpackZipFromBytes(InputStream zipped, String dstPath) + { + ArrayList files = new ArrayList(); + try { + String filename; + ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(zipped)); + ZipEntry zipEntry; + byte[] buffer = new byte[1024]; + int count; + + while ((zipEntry = zipInputStream.getNextEntry()) != null) + { + filename = zipEntry.getName(); + + if (zipEntry.isDirectory()) { + File directory = new File(dstPath + "/" + filename); + files.add(directory); + directory.mkdirs(); + continue; + } + + files.add(new File(dstPath + "/" + filename)); + FileOutputStream fileOutputStream = new FileOutputStream(dstPath + "/" + filename); + + while ((count = zipInputStream.read(buffer)) != -1) { + fileOutputStream.write(buffer, 0, count); + } + + fileOutputStream.close(); + zipInputStream.closeEntry(); + } + + zipInputStream.close(); + zipped.close(); + + } catch(IOException e) { + + e.printStackTrace(); + } + + return files; + } + + public static String getPgmStrings(Activity activity, String key) { + int resId = PluginUtil.getAppResource(activity, key, "string"); + Resources res = activity.getResources(); + return res.getString(resId); + } +} diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/drawable/dummy_my_location_button.png b/node_modules/cordova-plugin-googlemaps/src/android/res/drawable/dummy_my_location_button.png new file mode 100644 index 0000000..7fbcc2d Binary files /dev/null and b/node_modules/cordova-plugin-googlemaps/src/android/res/drawable/dummy_my_location_button.png differ diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/drawable/dummy_mylocation_button_shadow.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/drawable/dummy_mylocation_button_shadow.xml new file mode 100644 index 0000000..7c9ca4a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/drawable/dummy_mylocation_button_shadow.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/layout/dummy_infowindow.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/layout/dummy_infowindow.xml new file mode 100644 index 0000000..216d0ef --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/layout/dummy_infowindow.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-ar/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-ar/pgm_strings.xml new file mode 100644 index 0000000..384f5a8 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-ar/pgm_strings.xml @@ -0,0 +1,20 @@ + + + أغلق + مع مفتاح API الخاص بك \"GOOGLE_MAPS_ANDROID_API_KEY\" يرجى استبدال config.xml في منصات + غير متوفر لسبب ما على هذا الجهاز Google Maps Android API v2 + تم تكوين التطبيق بشكل خاطئ. هذا الخطأ غير قابل للاسترداد وسيتم التعامل معه على أنه مميت. يجب على مطور البرامج النظر في السجلات بعد ذلك لتحديد مزيد من المعلومات القابلة للتنفيذ + يرجى إعادة المحاولة ، ويجب أن تحل المشكلة ، Google Play حدث خطأ داخلي في خدمات + لقد حاولت الاتصال بالخدمة باسم حساب غير صالح + .التطبيق غير مرخص للمستخدم. هذا الخطأ غير قابل للاسترداد وسيتم التعامل معه على أنه مميت + .حدث خطأ في الشبكة. الرجاء المحاولة مرة أخرى ، ويجب أن تحل المشكلة + Google Play على هذا الجهاز. الرجاء تشغيل خدمات Google Play تم تعطيل الإصدار المثبت من خدمات + Google play من متجر Google Play المثبتة على هذا الجهاز غير أصلي. يرجى تحديث خدمات Google Play إصدار خدمات + Google Play مفقودة على هذا الجهاز. الرجاء تثبيت خدمات Google Play خدمات + Google Play من متجر Google Play قديم. يرجى تحديث خدمات Google Play الإصدار المثبت من خدمات + Google Play لقد حاولت الاتصال بالخدمة ولكنك لم تسجل الدخول. يرجى التحقق من تكوين خدمات + . نظرًا لأن هذا الجهاز لا يحتوي على أي موفر للموقع ، لا يمكن لهذا التطبيق اكتشاف موقعك + تم تعطيل خدمة الموقع + خدمة الموقع مرفوضة من قبل المستخدم + .لا يمكن الكشف عن موقعك. حاول ثانية + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-da/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-da/pgm_strings.xml new file mode 100644 index 0000000..83c063b --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-da/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Luk + Vær sød at ændre \'GOOGLE_MAPS_ANDROID_API_KEY\' i config.xml med din egen API-nøgle! + Google Maps Android API v2 er, af en eller anden grund, ikke tilgængelig på denne enhed. + Applikationen er fejlkonfigureret. Denne fejl er ikke genoprettelig og vil blive betragtet som fatal. Udvikleren bør kigge i logs efter dette for at finde mere handlingsrettet information. + En intern fejl skete med Google Play Services. Vær sød at prøve igen, og så burde det løse problemet. + Du prøvede at tilslutte til servicen med et invalidt kontonavn specificeret + Applikationen er ikke licenseret til brugeren. Denne fejl er ikke genoprettelig og vil blive betragtet som fatal. + Der skete en netværksfejl. Vær sød at prøve igen, og så burde det løse problemet. + Den installeret version af Google Play Services er blevet has been deaktiveret på denne enhed. Vær sød at tænde for Google Play Services. + Versionen for Google Play Services installeret på denne enhed er ikke autentisk. Vær sød at opdatere Google Play Services fra Google Play Store. + Google Play services mangler på denne enhed. Vær sød at installere Google Play Services. + Den installeret version af Google Play services er forældet. Vær sød at opdatere Google Play Services fra Google Play Store. + Du prøvede at tilslutte til servicen, men du er ikke logget ind. Vær sød at tjekke Google Play Services konfigurationen + Siden denne enhed ikke har en lokationsudbyder kan denne app ikke finde din lokation + Lokationsservice er deaktiveret. + Lokationsservice blev afvist af bruger. + Kan ikke finde din lokation. Prøv igen. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-de/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-de/pgm_strings.xml new file mode 100644 index 0000000..409207f --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-de/pgm_strings.xml @@ -0,0 +1,21 @@ + + + Schließen + Bitte ersetzen Sie \'GOOGLE_MAPS_ANDROID_API_KEY\' unter config.xml durch Ihren API Key! + Google Maps Android API v2 ist auf diesem Gerät aus einem unbekannten Grund nicht verfügbar. + Die Anwendung ist falsch konfiguriert und kann nicht fortgefahren werden. Der Entwickler muss die Protokolle inspezieren, um weitere Informationen zu erhalten. + Ein interner Fehler der Google Play-Dienste ist aufgetreten. Bitte versuchen Sie es erneut. + Sie haben versucht, eine Verbindung zum Dienst mit einem ungültigen Kontonamen herzustellen. + Die Anwendung ist für den Benutzer nicht lizenziert. Dieser Fehler ist nicht wiederherstellbar und wird als fatal behandelt. + Ein Netzwerkfehler ist aufgetreten. Bitte stellen Sie eine Internetverbindung her und versuchen Sie es erneut. + Die installierte Version der Google Play-Dienste wurde auf diesem Gerät deaktiviert. Bitte schalten Sie die Google Play-Dienste ein. + Die Version der auf diesem Gerät installierten Google Play-Dienste konnte nicht authentifiziert werden. Bitte Aktualisieren Sie die Google Play-Dienste im Google Play Store. + Google Play-Dienste fehlen auf diesem Gerät. Bitte installieren Sie die Google Play-Dienste über den Play Store. + + Die installierte Version der Google Play-Dienste ist veraltet. Aktualisieren Sie die Google Play-Dienste im Google Play Store. + Sie haben versucht, eine Verbindung mit dem Dienst herzustellen, aber Sie sind nicht angemeldet. Überprüfen Sie die Konfiguration der Google Play-Dienste + Da dieses Gerät keinen Standortanbieter hat, kann diese App Ihren Standort nicht orten. + Der Standortdienst ist deaktiviert. + Der Standortdienst wird vom Benutzer abgelehnt. + Sie konnten nicht lokalisiert werden. Versuchen Sie es bitte erneut. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-es/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-es/pgm_strings.xml new file mode 100644 index 0000000..29d1e54 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-es/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Cerrar + Por favor reemplace \'GOOGLE_MAPS_ANDROID_API_KEY\' en el archivo config.xml por su API Key! + Por alguna razón Google Maps Android API v2 no está disponible en este dispositivo. + La aplicación no está configurada correctamente. Ya que no es posible recuperarse de este error, será tratado como fatal. El desarrollador debe revisar los registros para determinar si existe alguna acción por realizar. + Ocurrió un error interno en Google Play Services. Por favor inténtalo de nuevo, y el problema debería resolverse. + Intentaste conectarte al servicio especificando un nombre de cuenta invalido. + La aplicación no tiene licencia para el usuario. Ya que no es posible recuperarse de este error, será tratado como fatal. + Ocurrió un error de red. Por favor inténtalo de nuevo, y el problema debería resolverse. + La versión instalada de Google Play Services ha sido deshabilitada en este dispositivo. Por favor encienda Google Play Services. + La versión instalada de Google Play Services no es autentica. Por favor actualice Google Play Services desde Google Play Store. + No se encontró Google Play Services en el dispositivo. Por favor instale Google Play Services. + La versión instalada de Google Play services está desactualizada. Por favor actualice Google Play Services desde Google Play Store. + Intentaste conectarte a los servicios pero no has iniciado sesión. Por favor revisa la configuración de Google Play Services + En vista de que este dispositivo no tiene ningún proveedor de localización, la aplicación no puede detectar tu ubicación. + Servicio de localización desactivado. + El servicio de localización fue rechazado por el usuario. + No se pudo detectar tu ubicación. Inténtalo de nuevo. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-fr/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-fr/pgm_strings.xml new file mode 100644 index 0000000..cb9ea4c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-fr/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Fermer + Veuillez remplacer \"GOOGLE_MAPS_ANDROID_API_KEY\" dans le fichier config.xml avec votre propre clé API! + Google Maps Android API v2 n\'est pas disponible sur cet appareil pour une raison non identifiée. + L\'application a mal été configurée. Cette erreur n\'est pas récupérable et sera traitée comme étant fatale. Le développeur devrait inspecter le journal d\'erreur afin de trouver des informations plus précises. + Une erreur interne est intervenue au niveau du Google Play Services. Veuillez réessayer, cela devrait résoudre le problème. + Vous avez tenté de vous connecter au service avec un nom de compte invalide. + L\'application ne contient pas de licence pour cet utilisateur. Cette erreur n\'est pas récupérable et sera traitée comme étant fatale. + Un problème de réseau est intervenu. Veuillez réessayer, cela devrait résoudre le problème. + La version du Google Play Services installée sur cet appareil a été désactivée. Veuillez activer le Google Play Services. + La version du Google Play Services installée sur cet appareil n\'est pas authentique. Merci de mettre à jour le Google Play Services à partir du Google Play Store. + Google Play Services n\'est pas installé sur cet appareil. Merci d\'installer le Google Play Services. + La version du Google Play Services installée sur cet appareil a expirée. Veuillez mettre à jour le Google Play Services à partir du Google Play Store. + Vous avez tenté de vous connecter au service, mais vous n\'est pas authentifié. Merci de vérifier la configuration du Google Play Services. + Cet appareil n\'est pas équipé d\'un service de localisation, cette application ne peut donc pas récupérer votre position. + Le service de localisation est désactivé. + Le service de localisation est rejeté par l\'utilisateur. + Localisation introuvable. Veuillez réessayer. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-hi/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-hi/pgm_strings.xml new file mode 100644 index 0000000..b7c3b76 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-hi/pgm_strings.xml @@ -0,0 +1,20 @@ + + + बंद + कृपया config.xml मे \'GOOGLE_MAPS_ANDROID_API_KEY\' को अपनी एपीआई कुंजी से बदलें + इस डिवाइस पर Google Maps Android API v2 उपलब्ध नहीं है + यह एप्लिकेशन गलत तरीके से कॉन्फ़िगर किया गया है. इस त्रुटि को ठीक किया नहीं जासकता इसे घातक माना जायेगा. डेवलपर को चाहिए वे लोग में देखकर कार्रवाई योग्य प्राप्त करे. + Google Play सेवाओं की एक आंतरिक त्रुटि हुई. कृप्या फिरसे प्रयास करें, इस से समस्या हल होनी चाहिए. + आप ने एक अमान्य खाता नाम के साथ सेवा से जुड़ने का प्रयास है. + इस उपयोगकर्ता के पास इस एप्लीकेशन लाइसेंस नहीं है. इस त्रुटि को ठीक किया नहीं जासकता इसे घातक माना जायेग. + एक नेटवर्क त्रुटि हुई. कृप्या फिरसे प्रयास करें, इस से समस्या हल होनी चाहिए. + इस डिवाइस पर Google Play सेवाओं का स्थापित संस्करण अक्षम है. कृपया Google Play सेवाएं चालू करें. + इस डिवाइस पर स्थापित Google Play सेवाओं का संस्करण प्रामाणिक नहीं है. कृपया Google Play Store से Google Play सेवाएं अपडेट करें. + इस डिवाइस पर Google Play सेवाएं मौजूद नहीं हैं. कृपया Google Play सेवाएं स्थापित करें. + Google Play सेवाओं का स्थापित संस्करण पुराना है. कृपया Google Play Store से Google Play सेवाएं अपडेट करें. + आप ने साइन किये बिना इस सेवा से जुड़ने का प्रयास किया है. कृपया Google Play सेवाएं कॉन्फ़िगरेशन जांचें + इस डिवाइस में कोई स्थान प्रदाता नहीं है, इसके कारण यह ऐप आपके स्थान का पता नहीं लगा सकता. + स्थान सेवा अक्षम है. + उपयोगकर्ता ने स्थान सेवा अस्वीकार कर दिया है. + आप के स्थान का पता नही चल रहा है. कृप्या फिरसे प्रयास करें. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-in/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-in/pgm_strings.xml new file mode 100644 index 0000000..be572cb --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-in/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Tutup + Silahkan ganti \'GOOGLE_MAPS_ANDROID_API_KEY\' di config.xml dengan Key API anda! + Google Maps Android API v2 tidak dapat digunakan pada perangkat ini. + Pengaturan applikasi ini salah. Kesalahan ini tidak dapat diperbaharui dan dianggap FATAL. Developer harus memeriksa informasi logs setelah ini untuk memastikan tidandakan. + terdapat kesalahan pada Google Play Services. Silahkan Coba lagi, untuk memperbaiki masalah. + Anda mencoba menghubungi service dengan nama akun yang tidak sesuai. + Appilkasi ini tidak diberikan licensi kepada pengguna. Error ini tidak dapat diperbaharui dan dianggap FATAL. + Kesalahan pada Jaringan. Silahkan Coba lagi,untuk memperbaiki masalah. + versi Google Play yang di install telah di non aktifkan pada perangkat ini. Silahkan Aktifkan Google Play Services. + Versi Google Play services yang di install pada perangkat ini tidak asli. Silahkan perbaharui Google Play Services melalui Google Play Store. + Google Play services tidak terdapat pada perangkat ini. Silahkan install Google Play Services. + Versi Google Play services sudha ketinggalan. Silahkan perbaharui Google Play Services melalui Google Play Store. + Anda mencoba terhubung pada service tetapi belum signed in. Silahkan periksa pengaturan Google Play Services + Karena perangkat ini tidak tersedia provider lokasi, Applikasi tidak dapat mengetahui lokasi anda. + Service Lokasi tidak aktif. + Service Lokasi Ditolak pengguna. + Tidak dapat mengetahui lokasi anda. Silahkan Coba lagi. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-ja/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-ja/pgm_strings.xml new file mode 100644 index 0000000..a6f775a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-ja/pgm_strings.xml @@ -0,0 +1,20 @@ + + + 閉じる + config.xmlファイル内の\'GOOGLE_MAPS_ANDROID_API_KEY\'をあなたのAPIキーに置き換えてください + この端末ではGoogle Maps Android API v2が利用できません。 + アプリケーションの設定が間違えています。このエラーは修復不可能かつ重大な失敗なものです。開発者は端末ログを確認してください。 + Google Play Servicesの内部エラーが発生しました。おそらくもう一度試みていただければ解決します。 + ログインしているGoogleアカウントが不正です。再度ログインして試してみてください。 + このアプリケーションの使用がユーザーに認められていません。このエラーは修復不可能かつ重大な失敗なものです。 + ネットワークエラーが発生しました。おそらくもう一度試みていただければ解決します。 + この端末にインストールされているGoogle Play Servicesが無効になっています。Google Play Servicesを有効にしてください。 + この端末にインストールされているGoogle Play Servicesでは認証ができません。Google Play Storeから最新版のGoogle Play Servicesをインストールしてください。 + この端末にGoogle Play Servicesがインストールされていません。Google Play Storeから最新版のGoogle Play Servicesをインストールしてください。 + この端末にインストールされているGoogle Play Servicesが古過ぎです。Google Play Storeから最新版のGoogle Play Servicesをインストールしてください。 + Google Maps Android API v2を使用しようと試みましたが、あなたがGoogleアカウントにログインしていません。Googleアカウントにログインしてから再度試してみてください。 + 利用できる位置情報を取得できる方法(GPSなど)がないため、この端末の位置情報が取得できませんでした。 + 位置情報サービス(GPSなど)が無効になっています。 + 位置情報サービス(GPSなど)の利用がユーザーにより拒否されました。 + この端末の位置情報が取得できませんでした。もう一度試してみてください。 + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-nb/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-nb/pgm_strings.xml new file mode 100644 index 0000000..84f830f --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-nb/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Lukk + Vennligst erstatt \'GOOGLE_MAPS_ANDROID_API_KEY\' i config.xml med din API-nøkkel! + Google Maps Android API v2 er ikke tilgjengelig av ukjent grunn på enheten. + Applikasjon er feilkonfigurert. Denne feilen er ikke gjenvinnbart og skal behandles som fatal. Utvikleren bør se på loggen etter dette for å fastslå mer handlingsbar informasjon. + Det har skjedd en intern feil av Google Play tjeneste. Vennligst prøv igjen og problemme skal løses ut. + Du prøver å koble til tjeneste med ugyldig brukkernavn. + Applikasjon har ikke licensing for dette brukkernavnet. Denne feilen er ikke gjenvinnbart og skal behandles som fatal. + Det har skjedd koblingsfailen. Vennligst prøv igjen og problemme skal løses ut. + Innstallert versjon av Google Play tjenesten har vært deaktivert på denne innhetten. Vennligst slå av Google Play tjenesten. + Innstallert versjon av Google Play tjenesten har ikke vært autentisk. Vennligst oppdater Google Play tjenesten fra Google Play Store. + Google Play tjenesten er utilghengelig på denne enheten. Vennligst installer Google Play tjenesten. + Innstallert verjson av Google Play tjenesten er ikke oppdatert. Vennligst oppdater Google Play tjenesten fra Google Play Store. + Du prøver å koble til tjeneste, men du har ikke logget inn. Vennligst sjekk Google Play tjenesten innstellinger. + Siden enheten har ingen stedleverandør, kan ikke din lokasjon oppdages av enheten. + Stedtjenester er deaktivert. + Stedtjenester er avvist av brukeren. + Kan ikke oppdage din lokasjon. Vannligst prøv igjen. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-nl/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-nl/pgm_strings.xml new file mode 100644 index 0000000..dad0c99 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-nl/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Sluiten + Vervang \"GOOGLE_MAPS_ANDROID_API_KEY\" in config.xml met je API Key! + Google Maps Android API v2 is niet beschikbaar op dit apparaat. + De applicatie is onjuist geconfigureerd. Deze foutmelding kan niet hesteld worden. De ontwikkelaar moet de logboek informatie bekijken om dit probleem op te lossen. + Er heeft een interne fout van Google Play Services plaatsgevonden. Probeer het opnieuw. + Je hebt geprobeerd om te verbinden met de service met een ongeldig gebruikersnaam. + Er is geen licentie voor de gebruiker. Deze foutmelding kan niet hersteld worden. + Er heeft een netwerkfout plaatsgevonden. Probeer het opnieuw. + De geinstalleerde versie van Google Play services staat uit op dit apparaat. Zet Google Play Services aan. + De geinstaleerde versie van Google Play services is niet autentiek. Update Google Play Services van de Google Play Store. + Google Play services ontbreekt op dit apparaat. Installeer Google Play Services. + De geinstalleerde Google Play services is niet langer geldig. Update Google Play Services van de Google Play Store. + Je probeert te verbinden met de service maar je bent niet ingelogd. Controleer de Google Play Services configuratie + Omdat dit apparaat geen locatie provider heeft kan de app je locatie niet bepalen. + Locatie service staat uit. + Locatie service is afgewezen door de gebruiker. + Kan je locatie niet bepalen. Probeer opnieuw. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-pl/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-pl/pgm_strings.xml new file mode 100644 index 0000000..722645a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-pl/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Zamknij + Proszę zastąpić \'GOOGLE_MAPS_ANDROID_API_KEY\' w pliku config.xml własnym kluczem API! + Google Maps Android API v2 . + Aplikacja posiada niepoprawną konfigurację. Tego błędu nie można odtworzyć i będzie on traktowany jako krytyczny. W celu uzyskania bardziej szczegółowych informacji programista powinien przejrzeć pliki z logami. + Wystąpił błąd wewnątrz usług Google Play. Ponowne uruchomienie powinno rozwiązać problem. + Próbę nawiązania połączenia z usługą nie powiodła się, z powodu nieprawidłowej nazwy konta. + Aplikacja nie jest licencjonowana dla użytkownika. Tego błędu nie można odtworzyć i będzie on traktowany jako krytyczny. + Wystąpił błąd połączenia internetowego. Proszę spróbować ponownie. + Zainstalowana wersja usług Google Play została wyłączona na urządzeniu. Proszę włączyć usługi Google Play. + Zainstalowana na urządzeniu wersja usług Google Play jest fałszywa. Proszę zaktualizować usługi Google Play ze sklepu Google Play. + Nie odnaleziono zainstalowanej wersji usług Google Play. Proszę zainstalować usługi Google Play. + Zainstalowana wersja usług Google Play jest przestarzała. Proszę zaktualizować usługi Google Play Services w sklepie Google Play. + Próbowano połączyć się z usługami Google Play nie będąc zalogowanym. Proszę sprawdzić konfigurację usług Google Play. + Na urządzeniu nie odnaleziono usług dostarczających położenie. Aplikacja nie będzie w stanie określić położenia. + Usługi lokalizacji są wyłączone. + Zgoda na usługi lokalizacji została odrzucona przez użytkownika. + Nie można określić położenia. Spróbuj ponownie. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-pt-rPT/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-pt-rPT/pgm_strings.xml new file mode 100644 index 0000000..1a202a7 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-pt-rPT/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Fechar + Por favor substitua \'GOOGLE_MAPS_ANDROID_API_KEY\' em config.xml com sua chave de API! + Google Maps Android API v2 não está disponível nesse dispositivo por alguma razão. + O aplicativo está mal configurado. Este erro não é recuperável e será tratado como fatal. O desenvolvedor deve analisar os logs para determinar o que pode ser feito + Ocorreu um erro interno no Google Play Services. Por favor tente novamente e o erro deverá estar resolvido. + Você tentou se conectar ao serviço com um nome de conta inválido. + O aplicativo não está licenciado para o usuário. Esse erro não é recuperável e será tratado como fatal. + Ocorreu um erro de rede. Por favor tente novamente e o erro deverá estar resolvido. + A versão instalada do Google Play Services foi desativada nesse dispositivo. Por favor ative o Google Play Services. + A versão do Google Play Services instalada nesse dispositivo não é autêntica. Atualize o Google Play Services na Google Play Store. + O Google Play services não existe nesse dispositivo. Por favor instale o Google Play Services. + A versão do Google Play Services instalada está desatualizada. Por favor atualize o Google Play Services na Google Play Store. + Você tentou se conectar ao serviço mas você não está logado. Por favor cheque a configuração do Google Play Services. + Esse dispositivo não tem nenhum provedor de localização, logo, este aplicativo não pode detectar sua localização. + O serviço de Localização está desativado. + O serviço de Localização foi rejeitado pelo usuário. + Não foi possível detectar sua localização. Tente novamente. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-ru/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-ru/pgm_strings.xml new file mode 100644 index 0000000..648accf --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-ru/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Закрыть + Пожалуйста, замените \'GOOGLE_MAPS_ANDROID_API_KEY\' в config.xml на Ваш API ключ! + По какой то причине Google Maps Android API v2 не доступен на этом устройстве. + Приложение неправильно настроено. Произошла фатальная ошибка. Разработчик приложения должен посмотреть в логи, чтобы исправить ошибку. + Произошла внутренняя ошибка Google Play Services. Пожалуйста, повторите попытку для решения проблемы. + Вы попытались подключиться к сервису с недопустимым именем учетной записи. + Произошла фатальная ошибка. Приложение не лицензировано для этого пользователя. + Произошла сетевая ошибка. Пожалуйста, повторите попытку для решения проблемы. + Установленная версия Google Play Services отключена на этом устройстве. Пожалуйста, включите Google Play Services. + Версия Google Play Services установлена на этом уствройстве не подлинная. Пожалуйста, обновите Google Play Services из Google Play Store. + Google Play Services отсутствует на этом устройстве. Пожалуйста, установите Google Play Services. + Установленная версия Google Play Services устаревшая. Пожалуйста, обновите Google Play Services из Google Play Store. + Вы попытались подключиться к сервису, но вы не вошли в систему. Пожалуйста, проверьте конфигурацию Google Play Services. + Поскольку это устройство не поддерживает поиск местоположения, это приложение не может определить Ваше расположение. + Сервис определения местоположения отключен. + Определение местоположения отклонено пользователем. + Не удается определить Ваше местоположение. Попробуй еще раз. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-uk/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-uk/pgm_strings.xml new file mode 100644 index 0000000..3c5d892 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-uk/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Закрити + Будь ласка, замініть \'GOOGLE_MAPS_ANDROID_API_KEY\' в config.xml на Ваш API ключ! + По якісь причині Google Maps Android API v2 не доступний на цьому пристрої. + Програма невірно налаштована. Виникла фатальна помилка. Розробник додатку повинен подивитись в логи, щоб виправити помилку. + Відбулася внутрішня помилка Google Play Services. Будь ласка, спробуйте ще раз для вирішення проблеми. + Ви спробували підключитися до сервісу з недійсним ім\'ям облікового запису. + Виникла фатальна помилка. Додаток не ліцензійований для користувача. + Виникла помилка мережі. Будь ласка, спробуйте ще раз для вирішення проблеми. + Встановлений Google Play Services на цьому пристрої - відключений. Будь ласка, увімкніть Google Play Services. + Встановлена версія Google Play Services на цьому пристрої не є автентична. Будь ласка, обновіть Google Play Services із Google Play Store. + Google Play Services відсутній на цьому пристрої. Будь ласка, встановіть Google Play Services. + Встановлена версія Google Play Services застаріла. Будь ласка, обновіть Google Play Services із Google Play Store. + Ви спробували підключитися до сервісу, але ви не ввійшли в систему. Будь ласка, перевірте конфігурацію Google Play Services. + Оскільки цей пристрій не підтримує пошук місцезнаходження, цей додаток не може визначити Ваше розташування. + Сервіс місцезнаходження вимкнений. + Сервіс пошуку місцезнаходження відхилений користувачем. + Не вдається виявити Ваше місцезнаходження. Спробуйте знову. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values-vi/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values-vi/pgm_strings.xml new file mode 100644 index 0000000..8566e92 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values-vi/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Đóng + Vui lòng thay \'GOOGLE_MAPS_ANDROID_API_KEY\' trong config.xml với API Key của bạn! + Google Maps Android API v2 vì một lý do nào đó không hoạt động trên thiết bị của bạn. + Ứng dụng chưa được cấu hình. Lỗi này là nghiêm trọng và làm cho hệ thống ngừng hoạt động. Nhà phát triển cần kiểm tra hệ thống Logs để có hướng khắc phục cụ thể.. + Lỗi xảy ra với dịch vụ Google Play. Vui lòng khởi động lại để hệ thống hoạt động bình thường.. + Bạn sử dụng tài khoản không hợp lệ để kết nối với dịch vụ. + Người sử dụng không được cấp phép sử dụng ứng dụng. Lỗi này là nghiêm trọng và làm cho hệ thống ngừng hoạt động. + Đường truyền mạng có lỗi. Vui lòng thử lại, và hệ thống sẽ hoạt động bình thường. + Dịch vụ Google Play không hoạt động trên thiết bị của bạn. Vui lòng bật dịch vụ Google Play để hệ thống hoạt động. + Dịch vụ Google Play cài đặt trên máy của bạn không hợp lệ. Vui lòng cập nhật dịch vụ Google Play trên Kho ứng dụng Google Play. + Dịch vụ Google PLay chưa được cài đặt trên thiết bị của bạn. Vui lòng cài đặt dịch vụ Google Play. + Dịch vụ Google Play của bạn đã lỗi thời. Vui lòng cập nhật dịch vụ Google Play trên Kho ứng dụng Google Play. + Bạn chưa thực hiện đăng nhập để kết nối với dịch vụ. Vui lòng xem lại cấu hình dịch vụ Google Play. + Thiết bị không hỗ trợ xác định vị trí vì vậy ứng dụng không thể xác định vị trí của bạn. + Dịch vụ xác định vị trí không được kích hoạt. + Người dùng không cho phép sử dụng dịch vụ xác định vị trí. + Không thể xác định vị trí của bạn. Vui lòng thử lại.. + diff --git a/node_modules/cordova-plugin-googlemaps/src/android/res/values/pgm_strings.xml b/node_modules/cordova-plugin-googlemaps/src/android/res/values/pgm_strings.xml new file mode 100644 index 0000000..77377e2 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/android/res/values/pgm_strings.xml @@ -0,0 +1,20 @@ + + + Close + Please replace \'GOOGLE_MAPS_ANDROID_API_KEY\' in the config.xml with your API Key! + Google Maps Android API v2 is not available for some reason on this device. + The application is miss-configured. This error is not recoverable and will be treated as fatal. The developer should look at the logs after this to determine more actionable information. + An internal error of Google Play Services occurred. Please retry, and it should resolve the problem. + You attempted to connect to the service with an invalid account name specified. + The application is not licensed to the user. This error is not recoverable and will be treated as fatal. + A network error occurred. Please retry, and it should resolve the problem. + The installed version of Google Play services has been disabled on this device. Please turn on Google Play Services. + The version of the Google Play services installed on this device is not authentic. Please update the Google Play Services from Google Play Store. + Google Play services is missing on this device. Please install the Google Play Services. + The installed version of Google Play services is out of date. Please update the Google Play Services from Google Play Store. + You attempted to connect to the service but you are not signed in. Please check the Google Play Services configuration + Since this device does not have any location provider, this app can not detect your location. + Location service is disabled. + Location service is rejected by user. + Can not detect your location. Try again. + diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/CordovaGoogleMaps.js b/node_modules/cordova-plugin-googlemaps/src/browser/CordovaGoogleMaps.js new file mode 100644 index 0000000..3993930 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/CordovaGoogleMaps.js @@ -0,0 +1,181 @@ + + + +var PluginMap = require('cordova-plugin-googlemaps.PluginMap'), + PluginStreetViewPanorama = require('cordova-plugin-googlemaps.PluginStreetViewPanorama'), + event = require('cordova-plugin-googlemaps.event'), + Environment = require('cordova-plugin-googlemaps.PluginEnvironment'); + +var MAPS = {}; + +var API_LOADED_STATUS = 0; // 0: not loaded, 1: loading, 2: completed + +document.addEventListener('load_googlemaps', function() { + var envOptions = Environment._getEnv(); + var API_KEY_FOR_BROWSER; + if (envOptions) { + if (location.protocol === 'https:') { + API_KEY_FOR_BROWSER = envOptions.API_KEY_FOR_BROWSER_RELEASE; + } else { + API_KEY_FOR_BROWSER = envOptions.API_KEY_FOR_BROWSER_DEBUG; + } + } + API_LOADED_STATUS = 1; + + var secureStripeScript = document.createElement('script'); + if (API_KEY_FOR_BROWSER && API_KEY_FOR_BROWSER.length > 35) { + secureStripeScript.setAttribute('src','https://maps.googleapis.com/maps/api/js?key=' + API_KEY_FOR_BROWSER); + } else { + // for development only + secureStripeScript.setAttribute('src','https://maps.googleapis.com/maps/api/js'); + } + secureStripeScript.addEventListener('load', function() { + API_LOADED_STATUS = 2; + + var mKeys = Object.keys(MAPS); + mKeys.forEach(function(mkey) { + var map = MAPS[mkey]; + if (!map.get('isGoogleReady')) { + map.trigger('googleready'); + } + }); + }); + + secureStripeScript.addEventListener('error', function(error) { + console.log('Can not load the Google Maps JavaScript API v3'); + console.log(error); + + var mKeys = Object.keys(MAPS); + mKeys.forEach(function(mkey) { + var map = MAPS[mkey]; + if (map) { + map.trigger('load_error'); + } + }); + }); + + document.getElementsByTagName('head')[0].appendChild(secureStripeScript); + +}, { + once: true +}); + +var stub = function(onSuccess) { + onSuccess(); +}; + +var CordovaGoogleMaps = { + resume: stub, + pause: stub, + getMap: function(onSuccess, onError, args) { + // memory cleanup + var mapIDs = Object.keys(MAPS); + mapIDs.forEach(function(mapId) { + var mapDivId = document.querySelector('[__pluginmapid=\'' + mapId + '\']'); + if (!mapDivId) { + if (MAPS[mapDivId]) { + MAPS[mapDivId].destroy(); + } + MAPS[mapDivId] = undefined; + delete MAPS[mapDivId]; + } + }); + + var meta = args[0], + mapId = meta.__pgmId; + args[0] = mapId; + args.unshift(this); + + var pluginMap = new (PluginMap.bind.apply(PluginMap, args)); + MAPS[mapId] = pluginMap; + var dummyObj = {}; + var keys = Object.getOwnPropertyNames(PluginMap.prototype).filter(function (p) { + return typeof PluginMap.prototype[p] === 'function'; + }); + keys.forEach(function(key) { + dummyObj[key] = pluginMap[key].bind(pluginMap); + }); + require('cordova/exec/proxy').add(mapId, dummyObj); + + pluginMap.one(event.MAP_READY, onSuccess); + pluginMap.one('load_error', onError); + + // Does this app already load the google maps library? + API_LOADED_STATUS = (window.google && window.google.maps) ? 2 : API_LOADED_STATUS; + + switch(API_LOADED_STATUS) { + case 0: + cordova.fireDocumentEvent('load_googlemaps', []); + break; + case 2: + pluginMap.trigger('googleready'); + break; + } + }, + removeMap: function(onSuccess, onError, args) { + var mapId = args[0]; + var pluginMap = MAPS[mapId]; + if (pluginMap) { + var map = pluginMap.get('map'); + google.maps.event.clearInstanceListeners(map); + var mapDiv = map.getDiv(); + if (mapDiv) { + mapDiv.parentNode.removeChild(mapDiv); + pluginMap.set('map', undefined); + } + } + pluginMap.destroy(); + pluginMap = null; + MAPS[mapId] = undefined; + delete MAPS[mapId]; + onSuccess(); + }, + + getPanorama: function(onSuccess, onError, args) { + // memory cleanup + var mapIDs = Object.keys(MAPS); + mapIDs.forEach(function(mapId) { + var mapDivId = document.querySelector('[__pluginmapid=\'' + mapId + '\']'); + if (!mapDivId) { + if (MAPS[mapDivId]) { + MAPS[mapDivId].destroy(); + } + MAPS[mapDivId] = undefined; + delete MAPS[mapDivId]; + } + }); + + var meta = args[0], + mapId = meta.__pgmId; + args[0] = mapId; + args.unshift(this); + + var pluginStreetView = new (PluginStreetViewPanorama.bind.apply(PluginStreetViewPanorama, args)); + MAPS[mapId] = pluginStreetView; + var dummyObj = {}; + var keys = Object.getOwnPropertyNames(PluginStreetViewPanorama.prototype).filter(function (p) { + return typeof PluginStreetViewPanorama.prototype[p] === 'function'; + }); + keys.forEach(function(key) { + dummyObj[key] = pluginStreetView[key].bind(pluginStreetView); + }); + require('cordova/exec/proxy').add(mapId, dummyObj); + + pluginStreetView.one(event.PANORAMA_READY, onSuccess); + pluginStreetView.one('load_error', onError); + + // Does this app already load the google maps library? + API_LOADED_STATUS = (window.google && window.google.maps) ? 2 : API_LOADED_STATUS; + + switch(API_LOADED_STATUS) { + case 0: + cordova.fireDocumentEvent('load_googlemaps', []); + break; + case 2: + pluginStreetView.trigger('googleready'); + break; + } + }, +}; + +require('cordova/exec/proxy').add('CordovaGoogleMaps', CordovaGoogleMaps); diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginCircle.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginCircle.js new file mode 100644 index 0000000..516e071 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginCircle.js @@ -0,0 +1,199 @@ + + + +var utils = require('cordova/utils'), + event = require('cordova-plugin-googlemaps.event'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'); + +function PluginCircle(pluginMap) { + var self = this; + BaseClass.apply(self); + Object.defineProperty(self, 'pluginMap', { + value: pluginMap, + writable: false + }); +} + +utils.extend(PluginCircle, BaseClass); + +PluginCircle.prototype._create = function(onSuccess, onError, args) { + var self = this, + map = self.pluginMap.get('map'), + circleId = 'circle_' + args[2], + pluginOptions = args[1]; + + var circleOpts = { + 'overlayId': circleId, + 'map': map + }; + + circleOpts.center = pluginOptions.center; + circleOpts.radius = pluginOptions.radius; + + if (Array.isArray(pluginOptions.strokeColor)) { + circleOpts.strokeColor = 'rgb(' + pluginOptions.strokeColor[0] + ',' + pluginOptions.strokeColor[1] + ',' + pluginOptions.strokeColor[2] + ')'; + circleOpts.strokeOpacity = pluginOptions.strokeColor[3] / 256; + } + if (Array.isArray(pluginOptions.fillColor)) { + circleOpts.fillColor = 'rgb(' + pluginOptions.fillColor[0] + ',' + pluginOptions.fillColor[1] + ',' + pluginOptions.fillColor[2] + ')'; + circleOpts.fillOpacity = pluginOptions.fillColor[3] / 256; + } + if ('width' in pluginOptions) { + circleOpts.strokeWeight = pluginOptions.width; + } + if ('zIndex' in pluginOptions) { + circleOpts.zIndex = pluginOptions.zIndex; + } + if ('visible' in pluginOptions) { + circleOpts.visible = pluginOptions.visible; + } + if ('clickable' in pluginOptions) { + circleOpts.clickable = pluginOptions.clickable; + } + + var circle = new google.maps.Circle(circleOpts); + circle.addListener('click', function(polyMouseEvt) { + self._onCircleEvent.call(self, circle, polyMouseEvt); + }); + + self.pluginMap.objects[circleId] = circle; + + onSuccess({ + '__pgmId': circleId + }); +}; + +PluginCircle.prototype.setCenter = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var lat = args[1]; + var lng = args[2]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + circle.setCenter(new google.maps.LatLng(lat, lng)); + } + onSuccess(); +}; + +PluginCircle.prototype.setFillColor = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var fillColor = args[1]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + + if (Array.isArray(fillColor)) { + circle.setOptions({ + 'fillColor': 'rgb(' + fillColor[0] + ',' + fillColor[1] + ',' + fillColor[2] + ')', + 'fillOpacity': fillColor[3] / 256 + }); + } + } + onSuccess(); +}; + +PluginCircle.prototype.setStrokeColor = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var strokeColor = args[1]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + + if (Array.isArray(strokeColor)) { + circle.setOptions({ + 'strokeColor': 'rgb(' + strokeColor[0] + ',' + strokeColor[1] + ',' + strokeColor[2] + ')', + 'strokeOpacity': strokeColor[3] / 256 + }); + } + } + onSuccess(); +}; + +PluginCircle.prototype.setStrokeWidth = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var width = args[1]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + circle.setOptions({ + 'strokeWeight': width + }); + } + onSuccess(); +}; + +PluginCircle.prototype.setRadius = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var radius = args[1]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + circle.setRadius(radius); + } + onSuccess(); +}; + +PluginCircle.prototype.setZIndex = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var zIndex = args[1]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + circle.setOptions({ + 'zIndex': zIndex + }); + } + onSuccess(); +}; + +PluginCircle.prototype.setVisible = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var visible = args[1]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + circle.setVisible(visible === true); + } + onSuccess(); +}; + +PluginCircle.prototype.setClickable = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var clickable = args[1]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + circle.setOptions({ + 'clickable': clickable === true + }); + } + onSuccess(); +}; + +PluginCircle.prototype.remove = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var circle = self.pluginMap.objects[overlayId]; + if (circle) { + google.maps.event.clearInstanceListeners(circle); + circle.setMap(null); + circle = undefined; + self.pluginMap.objects[overlayId] = undefined; + delete self.pluginMap.objects[overlayId]; + } + onSuccess(); +}; + +PluginCircle.prototype._onCircleEvent = function(circle, polyMouseEvt) { + var self = this, + mapId = self.pluginMap.__pgmId; + if (mapId in plugin.google.maps) { + plugin.google.maps[mapId]({ + 'evtName': event.CIRCLE_CLICK, + 'callback': '_onOverlayEvent', + 'args': [circle.overlayId, new plugin.google.maps.LatLng(polyMouseEvt.latLng.lat(), polyMouseEvt.latLng.lng())] + }); + } + +}; +module.exports = PluginCircle; diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginEnvironment.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginEnvironment.js new file mode 100644 index 0000000..59327d4 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginEnvironment.js @@ -0,0 +1,31 @@ + +var envOptions = {}; + +module.exports = { + 'isAvailable': function(onSuccess) { + onSuccess(); + }, + 'setBackGroundColor': function(onSuccess) { + // stub + onSuccess(); + }, + 'getLicenseInfo': function(onSuccess) { + // stub + onSuccess('cordova-plugin-googlemaps for browser does not need to display any open source lincenses. But for iOS, you still need to display the lincense.'); + }, + 'setEnv': function(onSuccess, onError, args) { + var options = args[0]; + var keys = Object.keys(options); + keys.forEach(function(key) { + envOptions[key] = options[key]; + }); + onSuccess(); + }, + '_getEnv': function() { + // internal method + return envOptions; + } +}; + + +require('cordova/exec/proxy').add('PluginEnvironment', module.exports); diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginGeocoder.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginGeocoder.js new file mode 100644 index 0000000..23b768c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginGeocoder.js @@ -0,0 +1,176 @@ + + +var BaseArrayClass = require('cordova-plugin-googlemaps.BaseArrayClass'); + +var geocoder = null; +var lastRequestTime = 0; +var QUEUE = new BaseArrayClass(); +QUEUE.on('insert_at', function() { + if (QUEUE.getLength() === 1) { + this.trigger('next'); + } +}); +QUEUE.one('insert_at', function() { + geocoder = new google.maps.Geocoder(); + this.trigger('next'); +}); +QUEUE.on('next', function() { + var self = QUEUE; + if (!geocoder || self._executing || QUEUE.getLength() === 0) { + return; + } + if (Date.now() - lastRequestTime < 300) { + setTimeout(function() { + self.trigger('next'); + }, 300 + Math.floor(Math.random() * 200)); + return; + } + lastRequestTime = Date.now(); + self._executing = true; + + var cmd = QUEUE.removeAt(0, true); + geocoder.geocode(cmd.geocoderRequest, function(results, status) { + switch(status) { + case google.maps.GeocoderStatus.ERROR: + cmd.onError('[geocoding] Cannot connect to Google servers'); + return; + case google.maps.GeocoderStatus.INVALID_REQUEST: + cmd.onError('[geocoding] Invalid request for geocoder'); + return; + case google.maps.GeocoderStatus.OVER_QUERY_LIMIT: + QUEUE.insertAt(0, cmd); + console.warn('[geocoding] Due to the OVER_QUERY_LIMIT error, wait 3 sec, then try again.'); + setTimeout(function() { + self._executing = false; + self.trigger('next'); + }, 3000 + Math.floor(Math.random() * 200)); + return; + case google.maps.GeocoderStatus.REQUEST_DENIED: + cmd.onError('[geocoding] Google denited your geocoding request.'); + return; + case google.maps.GeocoderStatus.UNKNOWN_ERROR: + cmd.onError('[geocoding] There was an unknown error. Please try again.'); + return; + } + + var pluginResults = results.map(function(geocoderResult) { + var result = { + 'position': { + 'lat': geocoderResult.geometry.location.lat(), + 'lng': geocoderResult.geometry.location.lng() + }, + extra: { + lines: [] + } + }; + if (geocoderResult.place_id) { + result.extra.place_id = geocoderResult.place_id; + } + if (geocoderResult.plus_code) { + result.extra.plus_code = geocoderResult.plus_code; + } + if (geocoderResult.types) { + result.extra.types = geocoderResult.types; + } + + var administrative_area = []; + var sublocality_area = []; + geocoderResult.address_components.forEach(function(addrComp) { + result.extra.lines.push(addrComp.long_name); + if (!result.locality && addrComp.types.indexOf('locality') > -1) { + result.locality = addrComp.short_name; + } + if (addrComp.types.indexOf('administrative_area_level_1') > -1 || + addrComp.types.indexOf('administrative_area_level_2') > -1 || + addrComp.types.indexOf('administrative_area_level_3') > -1 || + addrComp.types.indexOf('administrative_area_level_4') > -1) { + addrComp.types.forEach(function(type) { + if (type.indexOf('administrative_area_level_') === 0) { + var idx = parseInt(type.replace('administrative_area_level_', ''), 10); + administrative_area[idx - 1] = addrComp.long_name; + } + }); + } + if (addrComp.types.indexOf('sublocality_level_1') > -1 || + addrComp.types.indexOf('sublocality_level_2') > -1 || + addrComp.types.indexOf('sublocality_level_3') > -1 || + addrComp.types.indexOf('sublocality_level_4') > -1 || + addrComp.types.indexOf('sublocality_level_5') > -1) { + addrComp.types.forEach(function(type) { + if (type.indexOf('sublocality_level_') === 0) { + var idx = parseInt(type.replace('sublocality_level_', ''), 10); + sublocality_area[idx - 1] = addrComp.long_name; + } + }); + } + if (!result.country && addrComp.types.indexOf('country') > -1) { + result.country = addrComp.long_name; + result.countryCode = addrComp.short_name; + } + if (!result.postalCode && addrComp.types.indexOf('postal_code') > -1) { + result.postalCode = addrComp.long_name; + } + if (!result.thoroughfare && addrComp.types.indexOf('street_address') > -1) { + result.thoroughfare = addrComp.long_name; + } + }); + + if (administrative_area.length > 0) { + result.adminArea = administrative_area.shift(); + result.subAdminArea = administrative_area.join(','); + } + if (sublocality_area.length > 0) { + result.subLocality = sublocality_area.join(','); + } + //result.extra = geocoderResult.address_components; // for debug + + return result; + }); + + self._executing = false; + cmd.onSuccess({ + 'idx': cmd.pluginRequest.idx, + 'results': pluginResults + }); + + // Insert delay to prevent the OVER_QUERY_LIMIT error. + var delay = 300 + Math.floor(Math.random() * 200); + setTimeout(function() { + self._executing = false; + self.trigger('next'); + }, delay); + }); +}); + +module.exports = { + 'geocode': function(onSuccess, onError, args) { + var request = args[0]; + var geocoderRequest = {}; + + if (!request.position && request.address) { + //--------------------- + // Geocoding + //--------------------- + if (request.bounds && Array.isArray(request.bounds)) { + var bounds = new google.maps.LatLngBounds(); + request.bounds.forEach(function(position) { + bounds.extend(position); + }); + geocoderRequest.bounds = bounds; + } + geocoderRequest.address = request.address; + } + if (request.position && !request.address) { + geocoderRequest.location = request.position; + } + QUEUE.push({ + 'geocoderRequest': geocoderRequest, + 'pluginRequest': request, + 'onSuccess': onSuccess, + 'onError': onError + }); + } +}; + + +require('cordova/exec/proxy').add('PluginGeocoder', module.exports); diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginGroundOverlay.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginGroundOverlay.js new file mode 100644 index 0000000..4ea07f7 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginGroundOverlay.js @@ -0,0 +1,412 @@ + + + +var utils = require('cordova/utils'), + event = require('cordova-plugin-googlemaps.event'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'), + Spherical = require('cordova-plugin-googlemaps.spherical'), + LatLng = require('cordova-plugin-googlemaps.LatLng'); + +function PluginGroundOverlay(pluginMap) { + var self = this; + BaseClass.apply(self); + Object.defineProperty(self, 'pluginMap', { + value: pluginMap, + writable: false + }); +} + +utils.extend(PluginGroundOverlay, BaseClass); + +PluginGroundOverlay.prototype._create = function(onSuccess, onError, args) { + var self = this, + map = self.pluginMap.get('map'), + groundoverlayId = 'groundoverlay_' + args[2], + pluginOptions = args[1]; + + var groundoverlayOpts = { + 'overlayId': groundoverlayId, + 'map': map + }; + + var bounds = new google.maps.LatLngBounds(); + pluginOptions.bounds.forEach(function(latLng) { + bounds.extend(latLng); + }); + + if ('bearing' in pluginOptions) { + groundoverlayOpts.bearing = pluginOptions.bearing; + } + if ('zIndex' in pluginOptions) { + groundoverlayOpts.zIndex = pluginOptions.zIndex; + } + if ('opacity' in pluginOptions) { + groundoverlayOpts.opacity = pluginOptions.opacity; + } + if ('visible' in pluginOptions) { + groundoverlayOpts.visible = pluginOptions.visible; + } + if ('clickable' in pluginOptions) { + groundoverlayOpts.clickable = pluginOptions.clickable; + } + + var groundoverlay = newGroundOverlay(pluginOptions.url, bounds, groundoverlayOpts); + groundoverlay.addListener('click', function(polyMouseEvt) { + self._onGroundOverlayEvent.call(self, groundoverlay, polyMouseEvt); + }); + + self.pluginMap.objects[groundoverlayId] = groundoverlay; + self.pluginMap.objects['groundoverlay_property_' + groundoverlayId] = pluginOptions; + + + onSuccess({ + '__pgmId': groundoverlayId + }); +}; +PluginGroundOverlay.prototype.setImage = function(onSuccess, onError, args) { + var self = this, + overlayId = args[0], + url = args[1], + groundoverlay = self.pluginMap.objects[overlayId]; + + if (groundoverlay) { + groundoverlay.setImage(url); + } + + onSuccess(); +}; + +PluginGroundOverlay.prototype.setBearing = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var groundOverlay = self.pluginMap.objects[overlayId]; + if (groundOverlay) { + groundOverlay.setBearing(args[1]); + } + onSuccess(); +}; + +PluginGroundOverlay.prototype.setOpacity = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var groundOverlay = self.pluginMap.objects[overlayId]; + if (groundOverlay) { + groundOverlay.setOpacity(args[1]); + } + onSuccess(); +}; + +PluginGroundOverlay.prototype.setBounds = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var groundOverlay = self.pluginMap.objects[overlayId]; + if (groundOverlay) { + + var points = args[1]; + var bounds = new google.maps.LatLngBounds(); + points.forEach(function(latLng) { + bounds.extend(latLng); + }); + groundOverlay.setBounds(bounds); + } + onSuccess(); +}; + +PluginGroundOverlay.prototype.setZIndex = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var zIndex = args[1]; + var groundOverlay = self.pluginMap.objects[overlayId]; + if (groundOverlay) { + groundOverlay.setZIndex(zIndex); + } + onSuccess(); +}; + +PluginGroundOverlay.prototype.setClickable = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var groundOverlay = self.pluginMap.objects[overlayId]; + if (groundOverlay) { + groundOverlay.setClickable(args[1]); + } + onSuccess(); +}; +PluginGroundOverlay.prototype.setVisible = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var groundOverlay = self.pluginMap.objects[overlayId]; + if (groundOverlay) { + groundOverlay.setVisible(args[1]); + } + onSuccess(); +}; + +PluginGroundOverlay.prototype.remove = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var groundOverlay = self.pluginMap.objects[overlayId]; + if (groundOverlay) { + google.maps.event.clearInstanceListeners(groundOverlay); + groundOverlay.setMap(null); + groundOverlay.remove(); + groundOverlay = undefined; + self.pluginMap.objects[overlayId] = undefined; + delete self.pluginMap.objects[overlayId]; + delete self.pluginMap.objects['groundoverlay_property_' + overlayId]; + } + onSuccess(); +}; + +PluginGroundOverlay.prototype._onGroundOverlayEvent = function(groundoverlay, mouseEvt) { + var self = this, + mapId = self.pluginMap.__pgmId; + if (mapId in plugin.google.maps) { + plugin.google.maps[mapId]({ + 'evtName': event.GROUND_OVERLAY_CLICK, + 'callback': '_onOverlayEvent', + 'args': [groundoverlay.overlayId, new LatLng(mouseEvt.latLng.lat(), mouseEvt.latLng.lng())] + }); + } + +}; +module.exports = PluginGroundOverlay; + +function newGroundOverlay(url, bounds, options) { + + // https://github.com/apache/cordova-js/blob/c75e8059114255d1dbae1ede398e6626708ee9f3/src/common/utils.js#L167 + if (CustomGroundOverlay.__super__ !== google.maps.OverlayView.prototype) { + var key, defined = {}; + for (key in CustomGroundOverlay.prototype) { + defined[key] = CustomGroundOverlay.prototype[key]; + } + utils.extend(CustomGroundOverlay, google.maps.OverlayView); + for (key in defined) { + CustomGroundOverlay.prototype[key] = defined[key]; + } + } + + return new CustomGroundOverlay(url, bounds, options); +} + +function CustomGroundOverlay(url, bounds, options) { + google.maps.OverlayView.apply(this); + var self = this; + + //------------------------------------------------------------ + // Create an img element, then put it on map as GroundOverlay + //------------------------------------------------------------ + var img = new Image(); + img.src = url; + img.style.position = 'absolute'; + img.style.WebkitTransformOrigin = '50% 50%'; + img.style.MozTransformOrigin = '50% 50%'; + img.style.transformOrigin = '50% 50%'; + self.set('img', img); + + self.set('url', url); + self.addListener('url_changed', function() { + img.src = self.get('url'); + }); + + var path = new google.maps.MVCArray([ + new google.maps.LatLng(0, 0), + new google.maps.LatLng(0, 0), + new google.maps.LatLng(0, 0), + new google.maps.LatLng(0, 0) + ]); + self.set('path', path); + + self.addListener('bearing_changed', self.updateVertixes); + self.addListener('bounds_changed', self.updateVertixes); + self.addListener('opacity_changed', function() { + img.style.opacity = self.get('opacity'); + }); + + //--------------------------------------------------------- + // Create a transparent rectangle above the groundOverlay, + // then catch click event + //--------------------------------------------------------- + var touchPolygon = new google.maps.Polygon({ + 'map': options.map, + 'paths': path, + 'clickable': true, + 'fillColor': 'red', + 'fillOpacity': 0, + 'strokeOpacity': 0, + 'zIndex': options.zIndex || 0 + }); + self.addListener('visible_changed', function() { + img.style.visibility = self.get('visible') ? 'visible' : 'hidden'; + touchPolygon.setVisible(self.get('visible')); + }); + + self.addListener('zIndex_changed', function() { + touchPolygon.setOptions({ + 'zIndex': self.get('zIndex') + }); + img.style.zIndex = self.get('zIndex'); + }); + + touchPolygon.addListener('click', function(evt) { + if (self.get('clickable')) { + google.maps.event.trigger(self, 'click', { + 'latLng': evt. latLng + }); + } + }); + self.set('touchPolygon', touchPolygon); + + var markers = []; + for (var i = 0; i < 4; i++) { + markers.push(new google.maps.Marker({ + 'position': {'lat': 0, 'lng': 0}, + 'map': options.map + })); + } + self.set('markers', markers); + + + //--------------------------------------------------------- + // Apply option values + //--------------------------------------------------------- + for (var key in options) { + self.set(key, options[key]); + } + self.set('bounds', bounds); +} +CustomGroundOverlay.prototype.updateVertixes = function() { + var self = this; + var degree = self.get('bearing') || 0; + + // Get center + var bounds = self.get('bounds'); + if (!bounds) { + return; + } + var center = bounds.getCenter(), + centerPos = { + 'lat': center.lat(), + 'lng': center.lng() + }; + + // original NorthEast + var orgNE = bounds.getNorthEast(), + orgSW = bounds.getSouthWest(), + orgNEpos = { + 'lat': orgNE.lat(), + 'lng': orgNE.lng() + }; + var orgDrgreeNE = Spherical.computeHeading(centerPos, orgNEpos); + var orgDrgreeSE = Spherical.computeHeading(centerPos, { + 'lat': orgSW.lat(), + 'lng': orgNE.lng() + }); + + var distanceCenter2NE = Spherical.computeDistanceBetween(orgNEpos, centerPos); + // new NorthEast + var newNE = Spherical.computeOffsetOrigin(centerPos, distanceCenter2NE, degree + orgDrgreeNE); + + // new SourthEast + var newSE = Spherical.computeOffsetOrigin(centerPos, distanceCenter2NE, degree + orgDrgreeSE); + // new SourthWest + var newSW = Spherical.computeOffsetOrigin(centerPos, distanceCenter2NE, degree + orgDrgreeNE + 180); + // new SouthEast + var newNW = Spherical.computeOffsetOrigin(centerPos, distanceCenter2NE, degree + orgDrgreeSE + 180); + + // Caclulate bounds + var path = self.get('path'); + path.setAt(0, new google.maps.LatLng(newNE.lat, newNE.lng)); + path.setAt(1, new google.maps.LatLng(newSE.lat, newSE.lng)); + path.setAt(2, new google.maps.LatLng(newSW.lat, newSW.lng)); + path.setAt(3, new google.maps.LatLng(newNW.lat, newNW.lng)); + + self.draw(); +}; + +CustomGroundOverlay.prototype.remove = function() { + var self = this; + self.set('img', undefined); + google.maps.event.clearInstanceListeners(self); + google.maps.event.clearInstanceListeners(self.get('touchPolygon')); +}; + +CustomGroundOverlay.prototype.setClickable = function(clickable) { + var self = this; + self.set('clickable', clickable); +}; + +CustomGroundOverlay.prototype.setOpacity = function(opacity) { + var self = this; + self.get('img').style.opacity = opacity; +}; + +CustomGroundOverlay.prototype.setBearing = function(bearing) { + var self = this; + self.set('bearing', bearing); +}; + +CustomGroundOverlay.prototype.setBounds = function(bounds) { + var self = this; + self.set('bounds', bounds); +}; + +CustomGroundOverlay.prototype.setOpacity = function(opacity) { + var self = this; + self.set('opacity', opacity); +}; + +CustomGroundOverlay.prototype.setZIndex = function(zIndex) { + var self = this; + self.set('zIndex', zIndex); +}; + +CustomGroundOverlay.prototype.setVisible = function(visible) { + var self = this; + self.set('visible', visible); +}; + +CustomGroundOverlay.prototype.setImage = function(url) { + var self = this; + self.set('url', url); +}; + +CustomGroundOverlay.prototype.getBounds = function() { + var self = this; + return new google.maps.LatLngBounds( + self.get('sw'), self.get('ne') + ); +}; +CustomGroundOverlay.prototype.draw = function() { + var self = this, + projection = self.getProjection(); + if (!projection) { + return; + } + var bounds = self.get('bounds'), + img = self.get('img'); + + // Calculate positions + var swPx = projection.fromLatLngToDivPixel(bounds.getSouthWest()), + nePx = projection.fromLatLngToDivPixel(bounds.getNorthEast()); + + img.style.left = swPx.x + 'px'; + img.style.top = nePx.y + 'px'; + img.style.width = (nePx.x - swPx.x) + 'px'; + img.style.height = (swPx.y - nePx.y) + 'px'; + img.style.transform = 'rotate(' + self.get('bearing') + 'deg)'; + img.style.WebkitTransform = 'rotate(' + self.get('bearing') + 'deg)'; + img.style.MozTransform = 'rotate(' + self.get('bearing') + 'deg)'; +}; + +CustomGroundOverlay.prototype.onAdd = function() { + var self = this; + self.set('mapPane', self.getPanes().mapPane); + self.getPanes().mapPane.appendChild(self.get('img')); +}; + +CustomGroundOverlay.prototype.onRemove = function() { + var self = this; + self.get('mapPane').removeChild(self.get('img')); + google.maps.event.clearInstanceListeners(self.get('img')); +}; diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginKmlOverlay.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginKmlOverlay.js new file mode 100644 index 0000000..31e7ccd --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginKmlOverlay.js @@ -0,0 +1,545 @@ + +/* eslint no-useless-escape: off */ + +var InlineWorker = require('cordova-plugin-googlemaps.InlineWorker'); + +function PluginKmlOverlay() { + // stub +} + +PluginKmlOverlay.prototype._create = function(onSuccess, onError, args) { + var pluginOptions = args[1]; + + if (!/^https?:/.test(location.protocol)) { + return onError('KmlOverlay is only available on http: or https: protocols.'); + } + + //------------------------------------- + // Parse the xml file using WebWorker + //------------------------------------- + var worker = new InlineWorker(loadKml); + worker.onmessage = function(evt) { + //console.log('host message', evt.data); + worker.terminate(); + onSuccess(evt.data); + }; + worker.onerror = onError; + var link = document.createElement('a'); + link.href = pluginOptions.url; + var url = link.protocol+'//'+link.host+link.pathname+link.search; + worker.postMessage({ + 'url': url + }); +}; + +module.exports = PluginKmlOverlay; + +function loadKml(self) { + + // code: https://stackoverflow.com/q/32912732/697856 + var createCORSRequest = function(method, url, asynch) { + var xhr = new XMLHttpRequest(); + if ('withCredentials' in xhr) { + // XHR for Chrome/Firefox/Opera/Safari. + xhr.open(method, url, asynch); + // xhr.setRequestHeader('MEDIBOX', 'login'); + xhr.setRequestHeader('Content-Type', 'application/xml; charset=UTF-8'); + } else if (typeof window.XDomainRequest != 'undefined') { + // XDomainRequest for IE. + xhr = new window.XDomainRequest(); + xhr.open(method, url, asynch); + } else { + // CORS not supported. + xhr = null; + } + return xhr; + }; + + + //--------------------------------------- + // modified fromXML (xml parser) + //--------------------------------------- + var fromXML = (function() { + // fromXML + // https://github.com/kawanet/from-xml + // + // The MIT License (MIT) + // + // Copyright (c) 2016 Yusuke Kawasaki + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in all + // copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + // SOFTWARE. + + /** + * The fromXML() method parses an XML string, constructing the JavaScript + * value or object described by the string. + * + * @function fromXML + * @param text {String} The string to parse as XML + * @param [reviver] {Function} If a function, prescribes how the value + * originally produced by parsing is transformed, before being returned. + * @returns {Object} + */ + + var UNESCAPE = { + '&': '&', + '<': '<', + '>': '>', + ''': '\'', + '"': '"' + }; + + var CHILD_NODE_KEY = '#'; + + function parseXML(text) { + var list = String.prototype.split.call(text, /<([^!<>?](?:'[\S\s]*?'|"[\S\s]*?"|[^'"<>])*|!(?:--[\S\s]*?--|\[[^\[\]'"<>]+\[[\S\s]*?]]|DOCTYPE[^\[<>]*?\[[\S\s]*?]|(?:ENTITY[^"<>]*?"[\S\s]*?")?[\S\s]*?)|\?[\S\s]*?\?)>/); + var length = list.length; + + // root element + var root = {f: []}; + var elem = root; + + // dom tree stack + var stack = []; + + for (var i = 0; i < length;) { + // text node + var str = list[i++]; + if (str) appendText(str); + + // child node + var tag = list[i++]; + if (tag) parseNode(tag); + } + + return root; + + function parseNode(tag) { + var tagLength = tag.length; + var firstChar = tag[0]; + if (firstChar === '/') { + // close tag + var closed = tag.replace(/^\/|[\s\/].*$/g, '').toLowerCase(); + while (stack.length) { + var tagName = elem.n && elem.n.toLowerCase(); + elem = stack.pop(); + if (tagName === closed) break; + } + // } else if (firstChar === "?") { + // // XML declaration + // appendChild({n: "?", r: tag.substr(1, tagLength - 2)}); + } else if (firstChar === '!') { + if (tag.substr(1, 7) === '[CDATA[' && tag.substr(-2) === ']]') { + // CDATA section + appendText(tag.substr(8, tagLength - 10)); + } else { + // comment + appendChild({n: '!', r: tag.substr(1)}); + } + } else { + var child = openTag(tag); + appendChild(child); + if (tag[tagLength - 1] === '/') { + child.c = 1; // emptyTag + } else { + stack.push(elem); // openTag + elem = child; + } + } + } + + function appendChild(child) { + elem.f.push(child); + } + + function appendText(str) { + str = removeSpaces(str); + if (str) appendChild(unescapeXML(str)); + } + } + + function openTag(tag) { + var elem = {f: []}; + tag = tag.replace(/\s*\/?$/, ''); + var pos = tag.search(/[\s='"\/]/); + if (pos < 0) { + elem.n = tag; + } else { + elem.n = tag.substr(0, pos); + elem.t = tag.substr(pos); + } + return elem; + } + + function parseAttribute(elem, reviver) { + if (!elem.t) return; + var list = elem.t.split(/([^\s='"]+(?:\s*=\s*(?:'[\S\s]*?'|"[\S\s]*?"|[^\s'"]*))?)/); + var attributes = {}; + + list.forEach(function(str) { + var val; + str = removeSpaces(str); + if (!str) return; + + var pos = str.indexOf('='); + if (pos < 0) { + // bare attribute + val = null; + } else { + // attribute key/value pair + val = str.substr(pos + 1).replace(/^\s+/, ''); + str = str.substr(0, pos).replace(/\s+$/, ''); + + // quote: foo="FOO" bar='BAR' + var firstChar = val[0]; + var lastChar = val[val.length - 1]; + if (firstChar === lastChar && (firstChar === '\'' || firstChar === '"')) { + val = val.substr(1, val.length - 2); + } + + val = unescapeXML(val); + } + if (reviver) { + val = reviver(str, val); + } + addAttribute(attributes, str, val); + }); + + return attributes; + } + + function removeSpaces(str) { + return str && str.replace(/^\s+|\s+$/g, ''); + } + + function unescapeXML(str) { + return str.replace(/(&(?:lt|gt|amp|apos|quot|#(?:\d{1,6}|x[0-9a-fA-F]{1,5}));)/g, function(str) { + if (str[1] === '#') { + var code = (str[2] === 'x') ? parseInt(str.substr(3), 16) : parseInt(str.substr(2), 10); + if (code > -1) return String.fromCharCode(code); + } + return UNESCAPE[str] || str; + }); + } + + function toObject(elem, reviver) { + // + // var raw = elem.r; + // if (raw) return raw; + + var attributes = parseAttribute(elem, reviver); + var object; + var childList = elem.f; + var childLength = childList && childList.length || 0; + + if (attributes || childLength > 1) { + // merge attributes and child nodes + if (typeof attributes === 'object') { + object = attributes; + object.line=198; + } else { + object = {}; + } + object.tagName = elem.n; + childList.forEach(function(child) { + if ('string' === typeof child) { + addObject(object, CHILD_NODE_KEY, child); + } else { + addObject(object, child.n, toObject(child, reviver)); + } + }); + } else if (childLength) { + // the node has single child node but no attribute + var child = childList[0]; + if ('string' === typeof child) { + object = { + 'tagName': elem.n, + 'value': child, + 'line': 215 + }; + } else { + object = toObject(child, reviver); + if (child.n) { + object = { + 'tagName': elem.n, + 'value': { + 'children': [object] + }, + 'line': 227 + }; + } + } + } else { + // the node has no attribute nor child node + object = { + 'tagName': elem.n, + 'value': '', + 'line': 233 + }; + } + + if (reviver) { + object = reviver(elem.n || '', object); + } + + return object; + } + + function addAttribute(object, key, val) { + if ('undefined' === typeof val) return; + object.attributes = object.attributes || {}; + object.attributes[key] = val; + } + function addObject(object, key, val) { + if ('undefined' === typeof val) return; + object.value = object.value || {}; + object.value.children = object.value.children || []; + if (typeof val === 'object' && val.tagName) { + object.value.children.push(val); + } else { + object.value.children.push({ + 'tagName': key, + 'value': val, + 'line': 258 + }); + } + } + + return function(text, reviver) { + text = text.replace(/<\?xml[^>]+>/i, ''); + var xmlTree = parseXML(text); + var result = toObject(xmlTree, reviver); + result.tagName = 'document'; + return result; + }; + })(); + + //--------------------------------------- + // KmlParserClass + //--------------------------------------- + function KmlParserClass() { + var _parser = this; + _parser.styleHolder = {}; + _parser.schemaHolder = {}; + + _parser.tagSwitchTables = { + 'styleurl': _parser._styleurl, + 'stylemap': _parser._style, + 'style': _parser._style, + 'schema': _parser._schema, + 'coordinates': _parser._coordinates + }; + } + + KmlParserClass.prototype.parseXml = function(rootElement) { + var _parser = this, + tagName = rootElement.tagName.toLowerCase(); + + + + var _proc = _parser.tagSwitchTables[tagName] || _parser._default; + //console.log("--->tagName = " + tagName, tagName in _parser.tagSwitchTables ? tagName : '_default'); + var result = _proc.call(_parser, rootElement); + if (!result) { + return null; + } + result.tagName = tagName; + if (rootElement.attributes) { + var attrNames = Object.keys(rootElement.attributes); + attrNames.forEach(function(attrName) { + result[attrName] = rootElement.attributes[attrName]; + }); + } + return result; + }; + + KmlParserClass.prototype._styleurl = function(rootElement) { + return { + 'styleId': rootElement.value + }; + }; + + + KmlParserClass.prototype._style = function(rootElement) { + var _parser = this; + + // Generate a style id for the tag + var styleId = rootElement.attributes ? rootElement.attributes.id : null; + if (!styleId) { + styleId = '__' + Math.floor(Date.now() * Math.random()) + '__'; + } + var result = { + 'styleId': styleId + }; + + // Store style information into the styleHolder + var styles = {}; + var children = []; + if (rootElement.value.children) { + rootElement.value.children.forEach(function(childNode) { + var node = _parser.parseXml(childNode); + if (node.value) { + styles[node.tagName] = node.value; + } else { + children.push(node); + } + }); + if (children.length > 0) { + styles.children = children; + } + } + _parser.styleHolder[styleId] = styles; + return result; + }; + + KmlParserClass.prototype._schema = function(rootElement) { + var _parser = this; + var result = {}; + + // Generate a schema id for the tag + var schemaId = rootElement.attributes ? rootElement.attributes.id : null; + if (!schemaId) { + schemaId = '__' + Math.floor(Date.now() * Math.random()) + '__'; + } + + // Store schema information into the schemaHolder. + var schema = {}; + schema.name = rootElement.attributes ? rootElement.attributes.id : '__' + Math.floor(Date.now() * Math.random()) + '__'; + if (rootElement.value.children) { + var children = []; + rootElement.value.children.forEach(function(childNode) { + var node = _parser.parseXml(childNode); + if (node) { + children.push(node); + } + }); + if (children.length > 0) { + schema.children = children; + } + } + _parser.schemaHolder[schemaId] = schema; + + return result; + }; + + KmlParserClass.prototype._coordinates = function(rootElement) { + var result = {}; + var latLngList = []; + + var txt = rootElement.value; + txt = txt.replace(/\s+/g, '\n'); + txt = txt.replace(/\n+/g, '\n'); + var lines = txt.split(/\n/); + + lines.forEach(function(line) { + line = line.replace(/[^0-9,.\\-]/g, ''); + if (line !== '') { + var tmpArry = line.split(','); + latLngList.push({ + 'lat': parseFloat(tmpArry[1]), + 'lng': parseFloat(tmpArry[0]) + }); + } + }); + + result.coordinates = latLngList; + return result; + }; + + KmlParserClass.prototype._default = function(rootElement) { + var _parser = this, + result = {}; + if (!rootElement.value) { + return null; + } + + if (rootElement.value.children) { + var children = []; + rootElement.value.children.forEach(function(childNode) { + + var node = _parser.parseXml.call(_parser, childNode); + if (node) { + if ('styleId' in node) { + result.styleIDs = result.styleIDs || []; + result.styleIDs.push(node.styleId); + } else if (node.tagName !== 'schema') { + children.push(node); + } + } + + }); + result.children = children; + } else { + var value = rootElement.value; + if (/^-?[0-9]+$/.test(value)) { + result.value = parseInt(value, 10); + } else if (/^-?[0-9\.]+$/.test(value)) { + result.value = parseFloat(value, 10); + } else { + result.value = value; + } + } + return result; + }; + + + self.onmessage = function(evt) { + var params = evt.data; + //------------------------------------------ + // Load & parse kml file in WebWorker + //------------------------------------------ + (new Promise(function(resolve, reject) { + //----------------- + // Read XML file + //----------------- + var xhr = createCORSRequest('GET', params.url, true); + if (xhr) { + xhr.onreadystatechange = function() { + try { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + resolve(xhr.responseText); + } else { + reject(xhr); + } + } + } catch (e) { + reject(e.description); + } + }; + xhr.send(); + } + })) + .then(function(xmlTxt) { + //----------------- + // Parse it + //----------------- + var doc = fromXML(xmlTxt); + var parser = new KmlParserClass(); + var root = parser.parseXml(doc); + + var result = { + 'schemas': parser.schemaHolder, + 'styles': parser.styleHolder, + 'root': root + }; + postMessage(result); + }); + + }; +} diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginLocationService.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginLocationService.js new file mode 100644 index 0000000..3d07819 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginLocationService.js @@ -0,0 +1,59 @@ + +var LOCATION_ERROR = { + '1': 'service_denied', + '2': 'not_available', + '3': 'timeout' +}; + +module.exports = { + 'hasPermission': function(onSuccess, onError) { + if (navigator.permissions) { + navigator.permissions.query({'name': 'geolocation'}) + .then(function(permission) { + onSuccess(permission.state === 'granted' ? 1 : 0); + }) + .catch(onError); + } else { + onError('Browser does not support this feature.'); + } + }, + 'getMyLocation': function(onSuccess, onError) { + + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(function(position) { + onSuccess({ + 'latLng': { + 'lat': position.coords.latitude, + 'lng': position.coords.longitude + }, + 'elapsedRealtimeNanos': 0, + 'time': position.timestamp, + 'accuracy': position.coords.accuracy, + 'altitude': position.coords.altitude, + 'speed': position.coords.speed, + 'bearing': position.coords.heading, + 'provider': 'geolocationapi', + 'hashCode': 'dummy', + 'status': true + }); + }, function(error) { + onError({ + 'status': false, + 'error_code': LOCATION_ERROR[error.code], + 'error_message': error.message + }); + }, { + 'enableHighAccuracy': true + }); + } else { + onError({ + 'status': false, + 'error_code': 'not_available', + 'error_message': 'Since this device does not have any location provider, this app can not detect your location.' + }); + } + } +}; + + +require('cordova/exec/proxy').add('PluginLocationService', module.exports); diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginMap.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginMap.js new file mode 100644 index 0000000..44e92bd --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginMap.js @@ -0,0 +1,726 @@ + + + +var utils = require('cordova/utils'), + event = require('cordova-plugin-googlemaps.event'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'), + LatLng = require('cordova-plugin-googlemaps.LatLng'), + MapTypeId = require('cordova-plugin-googlemaps.MapTypeId'); + +var MAP_TYPES = {}; +MAP_TYPES[MapTypeId.NORMAL] = 'roadmap'; +MAP_TYPES[MapTypeId.ROADMAP] = 'roadmap'; +MAP_TYPES[MapTypeId.SATELLITE] = 'satellite'; +MAP_TYPES[MapTypeId.HYBRID] = 'hybrid'; +MAP_TYPES[MapTypeId.TERRAIN] = 'terrain'; +MAP_TYPES[MapTypeId.NONE] = 'none'; + +var LOCATION_ERROR = {}; +LOCATION_ERROR[1] = 'service_denied'; +LOCATION_ERROR[2] = 'not_available'; +LOCATION_ERROR[3] = 'cannot_detect'; +var LOCATION_ERROR_MSG = {}; +LOCATION_ERROR_MSG[1] = 'Location service is rejected by user.'; +LOCATION_ERROR_MSG[2] = 'Since this device does not have any location provider, this app can not detect your location.'; +LOCATION_ERROR_MSG[3] = 'Can not detect your location. Try again.'; + +function displayGrayMap(container) { + var gmErrorContent = document.querySelector('.gm-err-container'); + var gmnoprint = document.querySelector('.gmnoprint'); + if (!gmErrorContent && !gmnoprint) { + container.innerHTML = [ + '
', + '
', + '
', + '

Can not display map.
Check the developer console.

', + '
', + '
' + ].join('\n'); + } +} + +function PluginMap(mapId, options) { + var self = this; + BaseClass.apply(this); + var mapDiv = document.querySelector('[__pluginMapId=\'' + mapId + '\']'); + mapDiv.style.backgroundColor = 'rgb(229, 227, 223)'; + + var container = document.createElement('div'); + container.style.userSelect='none'; + container.style['-webkit-user-select']='none'; + container.style['-moz-user-select']='none'; + container.style['-ms-user-select']='none'; + mapDiv.style.position = 'relative'; + container.style.position = 'absolute'; + container.style.top = 0; + container.style.bottom = 0; + container.style.right = 0; + container.style.left = 0; + mapDiv.insertBefore(container, mapDiv.firstElementChild); + + self.set('isGoogleReady', false); + self.set('container', container); + self.PLUGINS = {}; + + Object.defineProperty(self, '__pgmId', { + value: mapId, + writable: false + }); + Object.defineProperty(self, 'objects', { + value: {}, + writable: false + }); + Object.defineProperty(self, 'activeMarker', { + value: null, + writable: true + }); + self.set('clickable', true); + + + self.one('googleready', function() { + self.set('isGoogleReady', true); + + var mapTypeReg = new google.maps.MapTypeRegistry(); + mapTypeReg.set('none', new google.maps.ImageMapType({ + 'getTileUrl': function() { return null; }, + 'name': 'none_type', + 'tileSize': new google.maps.Size(256, 256), + 'minZoom': 0, + 'maxZoom': 25 + })); + + var mapInitOptions = { + mapTypes: mapTypeReg, + mapTypeId: google.maps.MapTypeId.ROADMAP, + noClear: true, + zoom: 2, + minZoom: 2, + disableDefaultUI: true, + zoomControl: true, + center: {lat: 0, lng: 0} + }; + + if (options) { + if (options.mapType) { + mapInitOptions.mapTypeId = MAP_TYPES[options.mapType]; + } + if (options.styles) { + mapInitOptions.styles = JSON.parse(options.styles); + } + + if (options.controls) { + if (options.controls.zoom !== undefined) { + mapInitOptions.zoomControl = options.controls.zoom == true; + } + } + if (options.preferences) { + if (options.preferences.zoom) { + mapInitOptions.minZoom = options.preferences.zoom.minZoom; + if (options.preferences.zoom.maxZoom) { + mapInitOptions.maxZoom = options.preferences.zoom.maxZoom; + } + } + } + } + + var map = new google.maps.Map(container, mapInitOptions); + map.mapTypes = mapTypeReg; + self.set('map', map); + + var boundsLimit = null; + if (options.preferences && options.preferences.gestureBounds && + options.preferences.gestureBounds.length > 0) { + boundsLimit = new google.maps.LatLngBounds(); + options.preferences.gestureBounds.forEach(function(pos) { + boundsLimit.extend(pos); + }); + } + map.set('boundsLimit', boundsLimit); + + var timeoutError = setTimeout(function() { + self.trigger('load_error'); + displayGrayMap(mapDiv); + }, 3000); + + map.addListener('bounds_changed', function() { + var boundsLimit = map.get('boundsLimit'); + if (!boundsLimit) { + return; + } + var visibleBounds = map.getBounds(); + if (boundsLimit.intersects(visibleBounds) || + visibleBounds.contains(boundsLimit.getNorthEast()) && visibleBounds.contains(boundsLimit.getSouthWest()) || + boundsLimit.contains(visibleBounds.getNorthEast()) && boundsLimit.contains(visibleBounds.getSouthWest())) { + return; + } + var center = map.getCenter(); + var dummyLat = center.lat(), + dummyLng = center.lng(); + var ne = boundsLimit.getNorthEast(), + sw = boundsLimit.getSouthWest(); + if (dummyLat < sw.lat() ) { + dummyLat = sw.lat(); + } else if (dummyLat > ne.lat()) { + dummyLat = ne.lat(); + } + if (dummyLng < 0) { + // the Western Hemisphere + if (dummyLng > ne.lng()) { + dummyLng = ne.lng(); + } else if (dummyLng < sw.lng()) { + dummyLng = sw.lng(); + } + } else { + // the Eastern Hemisphere + if (dummyLng > ne.lng()) { + dummyLng = ne.lng(); + } else if (dummyLng < sw.lng()) { + dummyLng = sw.lng(); + } + } + var dummyLatLng = new google.maps.LatLng(dummyLat, dummyLng); + map.panTo(dummyLatLng); + }); + + google.maps.event.addListenerOnce(map, 'projection_changed', function() { + clearTimeout(timeoutError); + + self.trigger(event.MAP_READY); + map.addListener('idle', self._onCameraEvent.bind(self, 'camera_move_end')); + //map.addListener("bounce_changed", self._onCameraEvent.bind(self, 'camera_move')); + map.addListener('drag', self._onCameraEvent.bind(self, event.CAMERA_MOVE)); + map.addListener('dragend', self._onCameraEvent.bind(self, event.CAMERA_MOVE_END)); + map.addListener('dragstart', self._onCameraEvent.bind(self, event.CAMERA_MOVE_START)); + + map.addListener('click', function(evt) { + self._onMapEvent.call(self, event.MAP_CLICK, evt); + }); + map.addListener('mousedown', function() { + map.set('mousedown_time', Date.now()); + }); + map.addListener('mouseup', function(evt) { + if (Date.now() - (map.get('mousedown_time') || Date.now()) > 500) { + self._onMapEvent.call(self, event.MAP_LONG_CLICK, evt); + } + }); + map.addListener('drag', function(evt) { + self._onMapEvent.call(self, event.MAP_DRAG, evt); + }); + map.addListener('dragend', function(evt) { + self._onMapEvent.call(self, event.MAP_DRAG_END, evt); + }); + map.addListener('dragstart', function(evt) { + map.set('mousedown_time', undefined); + self._onMapEvent.call(self, event.MAP_DRAG_START, evt); + }); + }); + + if (options) { + if (options.camera) { + if (options.camera.target) { + + if (Array.isArray(options.camera.target)) { + var bounds = new google.maps.LatLngBounds(); + options.camera.target.forEach(function(pos) { + bounds.extend(pos); + }); + map.fitBounds(bounds, 5); + } else { + map.setCenter(options.camera.target); + } + if (typeof options.camera.tilt === 'number') { + map.setTilt(options.camera.tilt); + } + if (typeof options.camera.bearing === 'number') { + map.setHeading(options.camera.bearing); + } + if (typeof options.camera.zoom === 'number') { + map.setZoom(options.camera.zoom); + } + } + } else { + map.setCenter({lat: 0, lng: 0}); + } + } else { + map.setCenter({lat: 0, lng: 0}); + } + + }); + +} + +utils.extend(PluginMap, BaseClass); + +PluginMap.prototype.setOptions = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'), + options = args[0]; + + var mapInitOptions = {}; + + if (options) { + if (options.mapType) { + mapInitOptions.mapTypeId = MAP_TYPES[options.mapType]; + } + if (options.styles) { + mapInitOptions.styles = JSON.parse(options.styles); + } + + if (options.controls) { + if (options.controls.zoom !== undefined) { + mapInitOptions.zoomControl = options.controls.zoom == true; + } + } + if (options.preferences) { + if (options.preferences.zoom) { + mapInitOptions.minZoom = Math.max(options.preferences.zoom || 2, 2); + if (options.preferences.zoom.maxZoom) { + mapInitOptions.maxZoom = options.preferences.zoom.maxZoom; + } + } + + if ('gestureBounds' in options.preferences) { + var boundsLimit = null; + if (options.preferences.gestureBounds && options.preferences.gestureBounds.length > 0) { + boundsLimit = new google.maps.LatLngBounds(); + options.preferences.gestureBounds.forEach(function(pos) { + boundsLimit.extend(pos); + }); + } + map.set('boundsLimit', boundsLimit); + } + + } + } + map.setOptions(mapInitOptions); + + if (options) { + if (options.camera) { + if (options.camera.target) { + + if (Array.isArray(options.camera.target)) { + var bounds = new google.maps.LatLngBounds(); + options.camera.target.forEach(function(pos) { + bounds.extend(pos); + }); + map.fitBounds(bounds, 5); + } else { + map.setCenter(options.camera.target); + } + if (typeof options.camera.tilt === 'number') { + map.setTilt(options.camera.tilt); + } + if (typeof options.camera.bearing === 'number') { + map.setHeading(options.camera.bearing); + } + if (typeof options.camera.zoom === 'number') { + map.setZoom(options.camera.zoom); + } + } + } else { + map.setCenter({lat: 0, lng: 0}); + } + } else { + map.setCenter({lat: 0, lng: 0}); + } + + onSuccess(); +}; + +PluginMap.prototype.setActiveMarkerId = function(onSuccess, onError, args) { + var self = this, + markerId = args[0]; + self.activeMarker = self.objects[markerId]; + onSuccess(); +}; +PluginMap.prototype.clear = function(onSuccess) { + this.activeMarker = null; + onSuccess(); +}; + +PluginMap.prototype.getFocusedBuilding = function(onSuccess) { + // stub + onSuccess(-1); +}; + +PluginMap.prototype.setDiv = function(onSuccess, onError, args) { + var self = this, + map = self.get('map'), + container = self.get('container'); + + if (args.length === 0) { + if (container && container.parentNode) { + container.parentNode.removeAttribute('__pluginMapId'); + container.parentNode.removeChild(container); + } + } else { + var domId = args[0]; + var mapDiv = document.querySelector('[__pluginDomId=\'' + domId + '\']'); + mapDiv.style.position = 'relative'; + mapDiv.insertBefore(container, mapDiv.firstElementChild); + mapDiv.setAttribute('__pluginMapId', self.__pgmId); + } + + google.maps.event.trigger(map, 'resize'); + onSuccess(); +}; +PluginMap.prototype.resizeMap = function(onSuccess) { + var self = this; + var map = self.get('map'); + + google.maps.event.trigger(map, 'resize'); + onSuccess(); +}; + +PluginMap.prototype.panBy = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + map.panBy.apply(map, args); + onSuccess(); +}; + +PluginMap.prototype.setCameraBearing = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var heading = args[0]; + + map.setHeading(heading); + onSuccess(); +}; + +PluginMap.prototype.setCameraZoom = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var zoom = args[0]; + + map.setZoom(zoom); + onSuccess(); +}; + +PluginMap.prototype.setCameraTarget = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var lat = args[0], + lng = args[1]; + + map.setCenter(new google.maps.LatLng(lat, lng)); + onSuccess(); +}; + +PluginMap.prototype.setCameraTilt = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var tilt = args[0]; + + map.setTilt(tilt); + onSuccess(); +}; +PluginMap.prototype.setMyLocationEnabled = function(onSuccess) { + // stub + onSuccess(); +}; +PluginMap.prototype.stopAnimation = function(onSuccess) { + // stub + onSuccess(); +}; + +PluginMap.prototype.animateCamera = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + + var options = args[0]; + var padding = 'padding' in options ? options.padding : 5; + var bounds; + if (Array.isArray(options.target)) { + bounds = new google.maps.LatLngBounds(); + options.target.forEach(function(pos) { + bounds.extend(pos); + }); + map.fitBounds(bounds, padding); + } else { + var zoomFlag = typeof options.zoom === 'number'; + var targetFlag = !!options.target; + + if (zoomFlag && targetFlag) { + var projection = map.getProjection(); + var centerLatLng = new google.maps.LatLng(options.target.lat, options.target.lng, true); + var centerPoint = projection.fromLatLngToPoint(centerLatLng); + + var scale = Math.pow(2, options.zoom); + + var div = map.getDiv(); + var harfWidth = div.offsetWidth / 2; + var harfHeight = div.offsetHeight / 2; + var swPoint = new google.maps.Point((centerPoint.x * scale - harfWidth) / scale, (centerPoint.y * scale + harfHeight) / scale ); + var nePoint = new google.maps.Point((centerPoint.x * scale + harfWidth) / scale, (centerPoint.y * scale - harfHeight) / scale); + var sw = projection.fromPointToLatLng(swPoint); + var ne = projection.fromPointToLatLng(nePoint); + bounds = new google.maps.LatLngBounds(sw, ne); + map.fitBounds(bounds, padding); + + } else if (zoomFlag) { + map.setZoom(options.zoom); + } else if (targetFlag) { + map.panTo(options.target); + } + } + if (typeof options.tilt === 'number') { + map.setTilt(options.tilt); + } + if (typeof options.bearing === 'number') { + map.setHeading(options.bearing); + } + onSuccess(); + +}; + +PluginMap.prototype.moveCamera = function(onSuccess, onError, args) { + this.animateCamera.call(this, onSuccess, onError, args); +}; + +PluginMap.prototype.setMapTypeId = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var mapTypeId = args[0]; + map.setMapTypeId(MAP_TYPES[mapTypeId]); + onSuccess(); +}; +PluginMap.prototype.setClickable = function(onSuccess, onError, args) { + var self = this; + var clickable = args[0]; + self.set('clickable', clickable); + onSuccess(); +}; +PluginMap.prototype.setVisible = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var visibility = args[0]; + var mapDiv = map.getDiv(); + if (mapDiv) { + mapDiv.style.visibility = visibility === true ? 'visible' : 'hidden'; + } + onSuccess(); +}; + +PluginMap.prototype.setPadding = function(onSuccess) { + // stub + onSuccess(); +}; +PluginMap.prototype.setAllGesturesEnabled = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var enabled = args[0]; + map.setOptions({ + gestureHandling: enabled === true ? 'auto': 'none' + }); + + onSuccess(); +}; +PluginMap.prototype.setCompassEnabled = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var enabled = args[0]; + map.setOptions({ + rotateControl: enabled === true + }); + var mapTypeId = map.getMapTypeId(); + if (mapTypeId !== google.maps.MapTypeId.SATELLITE && + mapTypeId !== google.maps.MapTypeId.HYBRID) { + console.warn('map.setCompassEnabled() works only HYBRID or SATELLITE for this platform.'); + } + onSuccess(); +}; +PluginMap.prototype.setTrafficEnabled = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var enabled = args[0]; + + var trafficLayer = map.get('trafficLayer'); + if (!trafficLayer) { + trafficLayer = new google.maps.TrafficLayer(); + map.set('trafficLayer', trafficLayer); + } + if (enabled) { + trafficLayer.setMap(map); + } else { + trafficLayer.setMap(null); + } + + onSuccess(); + +}; + + + +PluginMap.prototype.fromLatLngToPoint = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var lat = args[0], + lng = args[1]; + + var projection = map.getProjection(), + bounds = map.getBounds(), + ne = bounds.getNorthEast(), + sw = bounds.getSouthWest(), + zoom = map.getZoom(), + north = ne.lat(), + west = sw.lng(); + + var nowrapFlag = !bounds.contains(new google.maps.LatLng(north, 179)); + + var scale = Math.pow(2, zoom), + topLeft = projection.fromLatLngToPoint(new google.maps.LatLng(north, west + 360, nowrapFlag)), + worldPoint = projection.fromLatLngToPoint(new google.maps.LatLng(lat, lng + 360, true)); + onSuccess([(worldPoint.x - topLeft.x) * scale, (worldPoint.y - topLeft.y) * scale]); +}; + +PluginMap.prototype.fromPointToLatLng = function(onSuccess, onError, args) { + var self = this; + var map = self.get('map'); + var x = args[0], + y = args[1]; + + var projection = map.getProjection(), + bounds = map.getBounds(), + ne = bounds.getNorthEast(), + sw = bounds.getSouthWest(), + zoom = map.getZoom(); + + var topRight = projection.fromLatLngToPoint(ne); + var bottomLeft = projection.fromLatLngToPoint(sw); + var scale = Math.pow(2, zoom); + var worldPoint = new google.maps.Point(x / scale + bottomLeft.x, y / scale + topRight.y); + var latLng = map.getProjection().fromPointToLatLng(worldPoint); + onSuccess([latLng.lat(), latLng.lng()]); + +}; + +PluginMap.prototype.setIndoorEnabled = function(onSuccess) { + // stub + onSuccess(); +}; + +PluginMap.prototype.toDataURL = function(onSuccess) { + // stub + onSuccess(); +}; + +PluginMap.prototype._syncInfoWndPosition = function() { + var self = this; + + if (!self.activeMarker) { + return; + } + + var latLng = self.activeMarker.getPosition(); + self.fromLatLngToPoint(function(point) { + + plugin.google.maps[self.__pgmId]({ + 'evtName': 'syncPosition', + 'callback': '_onSyncInfoWndPosition', + 'args': [{'x': point[0], 'y': point[1]}] + }); + + }, null, [latLng.lat(), latLng.lng()]); + +}; + +PluginMap.prototype._onMapEvent = function(evtName, evt) { + var self = this; + + if (self.get('clickable') === false && + (evtName === event.MAP_CLICK || evtName === event.MAP_LONG_CLICK)) { + evt.stop(); + return; + } + if (self.__pgmId in plugin.google.maps) { + if (evt) { + if (evtName === event.MAP_CLICK) { + if (evt.placeId) { + evt.stop(); + plugin.google.maps[self.__pgmId]({ + 'evtName': event.POI_CLICK, + 'callback': '_onMapEvent', + 'args': [evt.placeId, undefined, new LatLng(evt.latLng.lat(), evt.latLng.lng())] + }); + return; + } + } + plugin.google.maps[self.__pgmId]({ + 'evtName': evtName, + 'callback': '_onMapEvent', + 'args': [new LatLng(evt.latLng.lat(), evt.latLng.lng())] + }); + } else { + plugin.google.maps[self.__pgmId]({ + 'evtName': evtName, + 'callback': '_onMapEvent', + 'args': [] + }); + } + } + +}; + +PluginMap.prototype._onCameraEvent = function(evtName) { + var self = this, + map = self.get('map'), + center = map.getCenter(), + bounds = map.getBounds(), + ne = bounds.getNorthEast(), + sw = bounds.getSouthWest(); + + self._syncInfoWndPosition(); + + var cameraInfo = { + 'target': {'lat': center.lat(), 'lng': center.lng()}, + 'zoom': map.getZoom(), + 'tilt': map.getTilt() || 0, + 'bearing': map.getHeading() || 0, + 'northeast': {'lat': ne.lat(), 'lng': ne.lng()}, + 'southwest': {'lat': sw.lat(), 'lng': sw.lng()}, + 'farLeft': {'lat': ne.lat(), 'lng': sw.lng()}, + 'farRight': {'lat': ne.lat(), 'lng': ne.lng()}, // = northEast + 'nearLeft': {'lat': sw.lat(), 'lng': sw.lng()}, // = southWest + 'nearRight': {'lat': sw.lat(), 'lng': ne.lng()} + }; + if (self.__pgmId in plugin.google.maps) { + plugin.google.maps[self.__pgmId]({ + 'evtName': evtName, + 'callback': '_onCameraEvent', + 'args': [cameraInfo] + }); + } +}; + +PluginMap.prototype.loadPlugin = function(onSuccess, onError, args) { + var self = this; + var className = args[0]; + + var plugin; + if (className in self.PLUGINS) { + plugin = self.PLUGINS[className]; + } else { + var OverlayClass = require('cordova-plugin-googlemaps.Plugin' + className); + plugin = new OverlayClass(this); + self.PLUGINS[className] = plugin; + + // Since Cordova involes methods as Window, + // the `this` keyword of involved method is Window, not overlay itself. + // In order to keep indicate the `this` keyword as overlay itself, + // wrap the method. + var dummyObj = {}; + for (var key in OverlayClass.prototype) { + if (typeof OverlayClass.prototype[key] === 'function') { + dummyObj[key] = plugin[key].bind(plugin); + } else { + dummyObj[key] = plugin[key]; + } + } + require('cordova/exec/proxy').add(self.__pgmId + '-' + className.toLowerCase(), dummyObj); + } + + plugin._create.call(plugin, onSuccess, onError, args); +}; + +module.exports = PluginMap; diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginMarker.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginMarker.js new file mode 100644 index 0000000..cc7d3d6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginMarker.js @@ -0,0 +1,489 @@ + +var utils = require('cordova/utils'), + event = require('cordova-plugin-googlemaps.event'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'), + LatLng = require('cordova-plugin-googlemaps.LatLng'); + +function PluginMarker(pluginMap) { + var self = this; + BaseClass.apply(self); + Object.defineProperty(self, 'pluginMap', { + value: pluginMap, + writable: false + }); + Object.defineProperty(self, 'infoWnd', { + value: null, + writable: true + }); +} + +utils.extend(PluginMarker, BaseClass); + +PluginMarker.prototype._create = function(onSuccess, onError, args) { + var self = this, + markerId = 'marker_' + args[2], + pluginOptions = args[1]; + self.__create.call(self, markerId, pluginOptions, function(marker, properties) { + onSuccess(properties); + }, onError); +}; + +/*eslint-disable no-unused-vars*/ +PluginMarker.prototype.__create = function(markerId, pluginOptions, onSuccess, onError) { +/*eslint-enable no-unused-vars*/ + var self = this, + map = self.pluginMap.get('map'); + var markerOpts = { + 'overlayId': markerId, + 'position': pluginOptions.position, + 'disableAutoPan': pluginOptions.disableAutoPan, + 'draggable': pluginOptions.draggable, + 'visible': pluginOptions.visible + }; + + var iconSize = null; + if (pluginOptions.animation) { + markerOpts.animation = google.maps.Animation[pluginOptions.animation.toUpperCase()]; + } + if (pluginOptions.icon) { + var icon = pluginOptions.icon; + markerOpts.icon = {}; + if (Array.isArray(pluginOptions.icon) && pluginOptions.icon.length === 4) { + pluginOptions.icon = { + 'url': pluginOptions.icon + }; + } + if (typeof pluginOptions.icon === 'string') { + // Specifies path or url to icon image + markerOpts.icon.url = pluginOptions.icon; + } else if (typeof pluginOptions.icon === 'object') { + if (Array.isArray(pluginOptions.icon.url)) { + // Specifies color name or rule + markerOpts.icon = { + 'path': 'm12 0c-4.4183 2.3685e-15 -8 3.5817-8 8 0 1.421 0.3816 2.75 1.0312 3.906 0.1079 0.192 0.221 0.381 0.3438 0.563l6.625 11.531 6.625-11.531c0.102-0.151 0.19-0.311 0.281-0.469l0.063-0.094c0.649-1.156 1.031-2.485 1.031-3.906 0-4.4183-3.582-8-8-8zm0 4c2.209 0 4 1.7909 4 4 0 2.209-1.791 4-4 4-2.2091 0-4-1.791-4-4 0-2.2091 1.7909-4 4-4z', + 'fillColor': 'rgb(' + pluginOptions.icon.url[0] + ',' + pluginOptions.icon.url[1] + ',' + pluginOptions.icon.url[2] + ')', + 'fillOpacity': pluginOptions.icon.url[3] / 255, + 'scale': 1.3, + 'strokeWeight': 1, + 'strokeColor': 'rgb(255, 255, 255)', + 'strokeOpacity': 0.65, + 'anchor': new google.maps.Point(12, 27) + }; + iconSize = { + 'width': 22, + 'height': 28 + }; + } else { + markerOpts.icon.url = pluginOptions.icon.url; + if (pluginOptions.icon.size) { + markerOpts.icon.scaledSize = new google.maps.Size(icon.size.width, icon.size.height); + iconSize = icon.size; + } + } + if ('anchor' in icon && Array.isArray(icon.anchor)) { + markerOpts.icon.anchor = new google.maps.Point(icon.anchor[0], icon.anchor[1]); + } + } + + } + if (!markerOpts.icon || + !markerOpts.icon.url && !markerOpts.icon.path) { + // default marker + markerOpts.icon = { + 'path': 'm12 0c-4.4183 2.3685e-15 -8 3.5817-8 8 0 1.421 0.3816 2.75 1.0312 3.906 0.1079 0.192 0.221 0.381 0.3438 0.563l6.625 11.531 6.625-11.531c0.102-0.151 0.19-0.311 0.281-0.469l0.063-0.094c0.649-1.156 1.031-2.485 1.031-3.906 0-4.4183-3.582-8-8-8zm0 4c2.209 0 4 1.7909 4 4 0 2.209-1.791 4-4 4-2.2091 0-4-1.791-4-4 0-2.2091 1.7909-4 4-4z', + 'fillColor': 'rgb(255, 0, 0)', + 'fillOpacity': 1, + 'scale': 1.3, + 'strokeWeight': 1, + 'strokeColor': 'rgb(255, 255, 255)', + 'strokeOpacity': 0.65, + 'anchor': new google.maps.Point(12, 27) + }; + iconSize = { + 'width': 22, + 'height': 28 + }; + } + + if ('zIndex' in pluginOptions) { + markerOpts.zIndex = pluginOptions.zIndex; + } + markerOpts.map = map; + var marker = new google.maps.Marker(markerOpts); + marker.addListener('click', self.onMarkerClickEvent.bind(self, event.MARKER_CLICK, marker), {passive: true}); + marker.addListener('dragstart', self.onMarkerEvent.bind(self, event.MARKER_DRAG_START, marker)); + marker.addListener('drag', self.onMarkerEvent.bind(self, event.MARKER_DRAG, marker)); + marker.addListener('dragend', self.onMarkerEvent.bind(self, event.MARKER_DRAG_END, marker)); + + if (pluginOptions.title) { + marker.set('title', pluginOptions.title); + } + if (pluginOptions.snippet) { + marker.set('snippet', pluginOptions.snippet); + } + + + self.pluginMap.objects[markerId] = marker; + self.pluginMap.objects['marker_property_' + markerId] = markerOpts; + + if (iconSize) { + onSuccess(marker, { + '__pgmId': markerId, + 'width': iconSize.width, + 'height': iconSize.height + }); + } else { + var markerIcon = marker.getIcon(); + if (markerIcon && markerIcon.size) { + onSuccess({ + '__pgmId': markerId, + 'width': markerIcon.size.width, + 'height': markerIcon.size.height + }); + } else { + var img = new Image(); + img.onload = function() { + onSuccess(marker, { + '__pgmId': markerId, + 'width': img.width, + 'height': img.height + }); + }; + img.onerror = function(error) { + console.warn(error.getMessage()); + onSuccess(marker, { + '__pgmId': markerId, + 'width': 20, + 'height': 42 + }); + }; + if (typeof markerOpts.icon === 'string') { + img.src = markerOpts.icon; + } else { + img.src = markerOpts.icon.url; + } + } + } + + setTimeout(function() { + marker.setAnimation(null); + }, 500); +}; +PluginMarker.prototype._removeMarker = function(marker) { + marker.setMap(null); + marker = undefined; +}; + +PluginMarker.prototype.setDisableAutoPan = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var disableAutoPan = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.set('disableAutoPan', disableAutoPan); + } + onSuccess(); +}; +PluginMarker.prototype.setFlat = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var flat = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.set('flat', flat); + } + onSuccess(); +}; +PluginMarker.prototype.setVisible = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var visible = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.setVisible(visible); + } + onSuccess(); +}; +PluginMarker.prototype.setAnimation = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var animation = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.setAnimation(google.maps.Animation[animation]); + setTimeout(function() { + marker.setAnimation(null); + }, 500); + } + onSuccess(); +}; +PluginMarker.prototype.setRotation = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var rotation = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + var icon = marker.getIcon(); + if (icon && icon.path) { + icon.rotation = rotation; + marker.setIcon(icon); + } + } + onSuccess(); +}; +PluginMarker.prototype.setDraggable = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.setDraggable(args[1]); + } + onSuccess(); +}; +PluginMarker.prototype.setInfoWindowAnchor = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + var icon = marker.getIcon(); + var anchorX = args[1]; + anchorX = anchorX - icon.size.width / 2; + var anchorY = args[2]; + anchorY = anchorY - icon.size.height / 2; + marker.setOptions({ + 'anchorPoint': new google.maps.Point(anchorX, anchorY) + }); + if (self.infoWnd) { + self._showInfoWindow.call(self, marker); + } + } + onSuccess(); +}; +PluginMarker.prototype.setTitle = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var title = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.set('title', title); + } + onSuccess(); +}; + +PluginMarker.prototype.setSnippet = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var snippet = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.set('snippet', snippet); + } + onSuccess(); +}; + +PluginMarker.prototype.setOpacity = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var opacity = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.setOpacity(opacity); + } + onSuccess(); +}; + +PluginMarker.prototype.setIconAnchor = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var anchorX = args[1], + anchorY = args[2]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + var icon = marker.getIcon(); + if (typeof icon === 'string') { + icon = { + 'url': icon + }; + } + icon.anchor = new google.maps.Point(anchorX, anchorY); + marker.setIcon(icon); + } + onSuccess(); +}; +PluginMarker.prototype.setZIndex = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var zIndex = args[1]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.setZIndex(zIndex); + } + onSuccess(); +}; +PluginMarker.prototype.showInfoWindow = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + if (self.pluginMap.activeMarker && self.pluginMap.activeMarker !== marker) { + self.onMarkerClickEvent(event.INFO_CLICK, self.pluginMap.activeMarker); + } + self.pluginMap.activeMarker = marker; + self._showInfoWindow.call(self, marker); + } + onSuccess(); +}; +PluginMarker.prototype.hideInfoWindow = function(onSuccess) { + var self = this; + if (self.infoWnd) { + google.maps.event.trigger(self.infoWnd, 'closeclick'); + self.infoWnd.close(); + self.infoWnd = null; + } + onSuccess(); +}; +PluginMarker.prototype.setIcon = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var marker = self.pluginMap.objects[overlayId]; + + self.setIcon_(marker, args[1]) + .then(onSuccess) + .catch(onError); +}; +PluginMarker.prototype.setIcon_ = function(marker, iconOpts) { + return new Promise(function(resolve) { + if (marker) { + if (Array.isArray(iconOpts)) { + // Specifies color name or rule + iconOpts = { + 'path': 'm12 0c-4.4183 2.3685e-15 -8 3.5817-8 8 0 1.421 0.3816 2.75 1.0312 3.906 0.1079 0.192 0.221 0.381 0.3438 0.563l6.625 11.531 6.625-11.531c0.102-0.151 0.19-0.311 0.281-0.469l0.063-0.094c0.649-1.156 1.031-2.485 1.031-3.906 0-4.4183-3.582-8-8-8zm0 4c2.209 0 4 1.7909 4 4 0 2.209-1.791 4-4 4-2.2091 0-4-1.791-4-4 0-2.2091 1.7909-4 4-4z', + 'fillColor': 'rgb(' + iconOpts[0] + ',' + iconOpts[1] + ',' + iconOpts[2] + ')', + 'fillOpacity': iconOpts[3] / 256, + 'scale': 1.3, + 'strokeWeight': 0, + 'anchor': new google.maps.Point(12, 27) + }; + } else if (typeof iconOpts === 'object') { + + if (typeof iconOpts.size === 'object') { + iconOpts.size = new google.maps.Size(iconOpts.size.width, iconOpts.size.height, 'px', 'px'); + iconOpts.scaledSize = iconOpts.size; + } + if (Array.isArray(iconOpts.anchor)) { + iconOpts.anchor = new google.maps.Point(iconOpts.anchor[0], iconOpts.anchor[1]); + } + } + + marker.setIcon(iconOpts); + } + resolve(); + }); +}; +PluginMarker.prototype.setPosition = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + marker.setPosition({'lat': args[1], 'lng': args[2]}); + } + onSuccess(); +}; + +PluginMarker.prototype.remove = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var marker = self.pluginMap.objects[overlayId]; + if (marker) { + google.maps.event.clearInstanceListeners(marker); + marker.setMap(null); + marker = undefined; + self.pluginMap.objects[overlayId] = undefined; + delete self.pluginMap.objects[overlayId]; + delete self.pluginMap.objects['marker_property_' + overlayId]; + } + onSuccess(); +}; + +PluginMarker.prototype.onMarkerEvent = function(evtName, marker) { + var self = this, + mapId = self.pluginMap.__pgmId; + + if (mapId in plugin.google.maps) { + var latLng = marker.getPosition(); + plugin.google.maps[mapId]({ + 'evtName': evtName, + 'callback': '_onMarkerEvent', + 'args': [marker.overlayId, new LatLng(latLng.lat(), latLng.lng())] + }); + } + +}; + +PluginMarker.prototype._showInfoWindow = function(marker) { + var self = this; + if (!self.infoWnd) { + self.infoWnd = new google.maps.InfoWindow({ + 'pixelOffset': new google.maps.Size(0, 0) + }); + } + var container = document.createElement('div'); + if (self.pluginMap.activeMarker && self.pluginMap.activeMarker !== marker) { + self.onMarkerClickEvent(event.INFO_CLICK, self.pluginMap.activeMarker); + } + self.pluginMap.activeMarker = marker; + self.pluginMap._syncInfoWndPosition.call(self); + var maxWidth = marker.getMap().getDiv().offsetWidth * 0.7; + var html = []; + if (marker.get('title')) { + html.push(marker.get('title')); + } + if (marker.get('snippet')) { + html.push('' + marker.get('snippet') + ''); + } + if (html.length > 0) { + container.innerHTML = html.join('
'); + google.maps.event.addListenerOnce(self.infoWnd, 'domready', function() { + self.onMarkerClickEvent(event.INFO_OPEN, marker); + + if (container.parentNode) { + google.maps.event.addDomListener(container.parentNode.parentNode.parentNode, 'click', function() { + self.onMarkerClickEvent(event.INFO_CLICK, marker); + }, true); + } + + }); + self.infoWnd.setOptions({ + content: container, + disableAutoPan: marker.disableAutoPan, + maxWidth: maxWidth + }); + google.maps.event.addListener(self.infoWnd, 'closeclick', function() { + google.maps.event.clearInstanceListeners(self.infoWnd); + self.onMarkerClickEvent(event.INFO_CLOSE, marker); + }); + self.infoWnd.open(marker.getMap(), marker); + } +}; + +PluginMarker.prototype.onMarkerClickEvent = function(evtName, marker) { + var self = this; + + var overlayId = marker.get('overlayId'); + + if (self.pluginMap.activeMarker && self.pluginMap.activeMarker !== marker) { + self.onMarkerEvent(event.INFO_CLOSE, self.pluginMap.activeMarker); + } + self.pluginMap.activeMarker = marker; + if (marker.get('disableAutoPan') === false) { + self.pluginMap.get('map').panTo(marker.getPosition()); + } + if (overlayId.indexOf('markercluster_') > -1) { + self.onClusterEvent(evtName, marker); + } else { + self.onMarkerEvent(evtName, marker); + } + +}; + +module.exports = PluginMarker; diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginMarkerCluster.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginMarkerCluster.js new file mode 100644 index 0000000..68b304a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginMarkerCluster.js @@ -0,0 +1,714 @@ + +var utils = require('cordova/utils'), + event = require('cordova-plugin-googlemaps.event'), + PluginMarker = require('cordova-plugin-googlemaps.PluginMarker'), + Thread = require('cordova-plugin-googlemaps.Thread'), + LatLng = require('cordova-plugin-googlemaps.LatLng'), + BaseArrayClass = require('cordova-plugin-googlemaps.BaseArrayClass'); + +var STATUS = { + 'WORKING': 0, + 'CREATED': 1, + 'DELETED': 2 +}; + +function PluginMarkerCluster(pluginMap) { + var self = this; + PluginMarker.call(self, pluginMap); + + Object.defineProperty(self, 'pluginMarkers', { + value: {}, + writable: false + }); + Object.defineProperty(self, 'debugFlags', { + value: {}, + writable: false + }); + + var deleteMarkers = new BaseArrayClass(); + Object.defineProperty(self, 'deleteMarkers', { + value: deleteMarkers, + writable: false + }); + + deleteMarkers.on('insert_at', function() { + var key = deleteMarkers.removeAt(0); + var marker = self.pluginMap.objects[key]; + if (marker) { + self._removeMarker(marker); + } + + self.pluginMap.objects[key] = undefined; + self.pluginMap.objects['marker_property_' + key] = undefined; + self.pluginMarkers[key] = undefined; + + delete self.pluginMap.objects[key]; + delete self.pluginMap.objects['marker_property_' + key]; + delete self.pluginMarkers[key]; + self.pluginMarkers[key] = STATUS.DELETED; + }); +} + +utils.extend(PluginMarkerCluster, PluginMarker); + +PluginMarkerCluster.prototype._create = function(onSuccess, onError, args) { + var self = this, + params = args[1], + hashCode = args[2], + positionList = params.positionList; + + //--------------------------------------------- + // Calculate geocell hashCode on multi thread + //--------------------------------------------- + var tasks = []; + while(positionList.length > 0) { + var list = positionList.splice(0, 10); + + tasks.push(new Promise(function(resolve, reject) { + + var thread = new Thread(function(params) { + + + + var GEOCELL_GRID_SIZE = 4; + var GEOCELL_ALPHABET = '0123456789abcdef'; + + function getGeocell(lat, lng, resolution) { + var north = 90.0, + south = -90.0, + east = 180.0, + west = -180.0, + subcell_lng_span, subcell_lat_span, + x, y, cell = []; + + while(cell.length < resolution + 1) { + subcell_lng_span = (east - west) / GEOCELL_GRID_SIZE; + subcell_lat_span = (north - south) / GEOCELL_GRID_SIZE; + + x = Math.min(Math.floor(GEOCELL_GRID_SIZE * (lng - west) / (east - west)), GEOCELL_GRID_SIZE - 1); + y = Math.min(Math.floor(GEOCELL_GRID_SIZE * (lat - south) / (north - south)), GEOCELL_GRID_SIZE - 1); + cell.push(_subdiv_char(x, y)); + + south += subcell_lat_span * y; + north = south + subcell_lat_span; + + west += subcell_lng_span * x; + east = west + subcell_lng_span; + } + return cell.join(''); + } + function _subdiv_char(posX, posY) { + return GEOCELL_ALPHABET.charAt( + (posY & 2) << 2 | + (posX & 2) << 1 | + (posY & 1) << 1 | + (posX & 1) << 0); + } + + + + + + + return params.positionList.map(function(position) { + return getGeocell(position.lat, position.lng, params.resolution); + }); + }); + + thread + .once({ + 'positionList': list, + 'resolution': 12 + }) + .done(resolve) + .fail(reject); + })); + } + + Promise.all(tasks) + .then(function(results) { + var id = 'markerclister_' + hashCode; + self.debugFlags[id] = params.debug; + + var result = { + 'geocellList': Array.prototype.concat.apply([], results), + 'hashCode': hashCode, + '__pgmId': id + }; + + onSuccess(result); + }) + .catch(onError); + +}; + + +PluginMarkerCluster.prototype.redrawClusters = function(onSuccess, onError, args) { + var self = this; + + var updateClusterIDs = [], + changeProperties = {}, + clusterId = args[0], + isDebug = self.debugFlags[clusterId], + params = args[1], + map = self.pluginMap.get('map'); + + if ('new_or_update' in params) { + + //--------------------------- + // Determine new or update + //--------------------------- + params.new_or_update.forEach(function(clusterData) { + var positionJSON = clusterData.position, + markerId = clusterData.__pgmId, + clusterId_markerId = clusterId + '-' + markerId; + + // Save the marker properties + self.pluginMap.objects['marker_property_' + clusterId_markerId] = clusterData; + + // Set the WORKING status flag + self.pluginMarkers[clusterId_markerId] = STATUS.WORKING; + updateClusterIDs.push(clusterId_markerId); + + // Prepare the marker properties for addMarker() + var properties = { + 'lat': positionJSON.lat, + 'lng': positionJSON.lng, + '__pgmId': clusterId_markerId + }; + if ('title' in clusterData) { + properties.title = clusterData.title; + } + + if ('icon' in clusterData) { + var iconObj = clusterData.icon, + iconProperties = {}, + label; + if (typeof iconObj === 'string') { + iconProperties.url = iconObj; + properties.icon = iconProperties; + } else if (typeof iconObj === 'object') { + iconProperties = iconObj; + if (clusterData.isClusterIcon) { + if (iconObj.label) { + label = iconObj.label; + if (isDebug) { + label.text = markerId; + } else { + label.text = clusterData.count; + } + } else { + label = {}; + if (isDebug) { + label.text = markerId; + } else { + label.fontSize = 15; + label.bold = true; + label.text = clusterData.count; + } + } + iconProperties.label = label; + } + if ('anchor' in iconObj) { + iconProperties.anchor = iconObj.anchor; + } + if ('infoWindowAnchor' in iconObj) { + iconProperties.anchor = iconObj.infoWindowAnchor; + } + properties.icon = iconProperties; + } + } + changeProperties[clusterId_markerId] = JSON.parse(JSON.stringify(properties)); + }); + + if (updateClusterIDs.length === 0) { + self.deleteProcess(clusterId, params); + onSuccess({}); + return; + } + + //--------------------------- + // mapping markers on the map + //--------------------------- + var allResults = {}; + + //--------------- + // new or update + //--------------- + var tasks = []; + updateClusterIDs.forEach(function(clusterId_markerId) { + self.pluginMarkers[clusterId_markerId] = STATUS.WORKING; + var isNew = !(clusterId_markerId in self.pluginMap.objects); + + // Get the marker properties + var markerProperties = changeProperties[clusterId_markerId], + properties, marker; + + if (clusterId_markerId.indexOf('-marker_') > -1) { + //------------------- + // regular marker + //------------------- + if (isNew) { + properties = self.pluginMap.objects['marker_property_' + clusterId_markerId]; + + tasks.push(new Promise(function(resolve) { + + self.__create.call(self, clusterId_markerId, { + 'position': properties.position, + 'icon': properties.icon, + 'disableAutoPan': properties.disableAutoPan + }, function(marker, properties) { + if (markerProperties.title) { + marker.set('title', markerProperties.title); + } + if (markerProperties.snippet) { + marker.set('snippet', markerProperties.snippet); + } + if (self.pluginMarkers[clusterId_markerId] === STATUS.DELETED) { + self._removeMarker.call(self, marker); + delete self.pluginMarkers[clusterId_markerId]; + resolve(); + } else { + self.pluginMarkers[clusterId_markerId] = STATUS.CREATED; + allResults[clusterId_markerId.split('-')[1]] = { + 'width': properties.width, + 'height': properties.height + }; + resolve(); + } + }); + })); + + } else { + marker = self.pluginMap.objects[clusterId_markerId]; + //---------------------------------------- + // Set the title and snippet properties + //---------------------------------------- + if (markerProperties.title) { + marker.set('title', markerProperties.title); + } + if (markerProperties.snippet) { + marker.set('snippet', markerProperties.snippet); + } + if (self.pluginMarkers[clusterId_markerId] === STATUS.DELETED) { + self._removeMarker.call(self, marker); + delete self.pluginMarkers[clusterId_markerId]; + } else { + self.pluginMarkers[clusterId_markerId] = STATUS.CREATED; + } + + } + } else { + //-------------------------- + // cluster icon + //-------------------------- + if (isNew) { + // If the requested id is new location, create a marker + marker = newClusterIcon({ + 'map': map, + 'position': { + 'lat': markerProperties.lat, + 'lng': markerProperties.lng + }, + 'overlayId': clusterId_markerId, + 'opacity': 0 + }); + marker.addListener('click', self.onClusterEvent.bind(self, 'cluster_click', marker)); + + // Store the marker instance with markerId + self.pluginMap.objects[clusterId_markerId] = marker; + + } else { + marker = self.pluginMap.objects[clusterId_markerId]; + } + //---------------------------------------- + // Set the title and snippet properties + //---------------------------------------- + if (markerProperties.title) { + marker.set('title', markerProperties.title); + } + if (markerProperties.snippet) { + marker.set('snippet', markerProperties.snippet); + } + if (markerProperties.icon) { + var icon = markerProperties.icon; + + tasks.push(new Promise(function(resolve) { + + self.setIconToClusterMarker.call(self, clusterId_markerId, marker, icon) + .then(function() { + //-------------------------------------- + // Marker was updated + //-------------------------------------- + marker.setVisible(true); + self.pluginMarkers[clusterId_markerId] = STATUS.CREATED; + resolve(); + }) + .catch(function(error) { + //-------------------------------------- + // Could not read icon for some reason + //-------------------------------------- + if (marker.get('overlayId')) { + self._removeMarker.call(self, marker); + } + self.pluginMarkers[clusterId_markerId] = STATUS.DELETED; + + console.warn(error.getMessage()); + self.deleteMarkers.push(clusterId_markerId); + resolve(); + }); + + })); + + } else { + //-------------------- + // No icon for marker + //-------------------- + self.pluginMarkers[clusterId_markerId] = STATUS.CREATED; + } + } + }); + Promise.all(tasks).then(function() { + self.deleteProcess(clusterId, params); + onSuccess(allResults); + }).catch(onError); + } +}; + +PluginMarkerCluster.prototype.deleteProcess = function(clusterId, params) { + var self = this; + if (!params.delete || params.delete.length === 0) { + return; + } + params.delete.forEach(function(key) { + self.deleteMarkers.push(clusterId + '-' + key); + }); +}; + +PluginMarkerCluster.prototype.setIconToClusterMarker = function(markerId, marker, iconProperty) { + var self = this; + return new Promise(function(resolve, reject) { + + if (self.pluginMarkers[markerId] === STATUS.DELETED) { + self._removeMarker.call(self, marker); + delete self.pluginMap.objects[markerId]; + delete self.pluginMap.objects['marker_property_' + markerId]; + + delete self.pluginMarkers[markerId]; + reject('marker has been removed'); + return; + } + self.setIcon_.call(self, marker, iconProperty) + .then(function() { + if (self.pluginMarkers[markerId] === STATUS.DELETED) { + self._removeMarker.call(self, marker); + delete self.pluginMap.objects[markerId]; + delete self.pluginMap.objects['marker_property_' + markerId]; + self.pluginMarkers.remove(markerId); + resolve(); + return; + } + marker.setVisible(true); + self.pluginMarkers[markerId] = STATUS.CREATED; + resolve(); + }); + }); + +}; + +PluginMarkerCluster.prototype.remove = function(onSuccess, onError, args) { + var self = this, + clusterId = args[0], + keys = Object.keys(self.pluginMarkers); + + keys.forEach(function(key) { + if (key.indexOf(clusterId) === 0) { + if (self.pluginMap.objects[key]) { + self.pluginMap.objects[key].setMap(null); + } + self.pluginMarkers[key] = STATUS.DELETED; + delete self.pluginMap.objects[key]; + delete self.pluginMap.objects['marker_property_' + key]; + } + }); + onSuccess(); +}; + + +PluginMarkerCluster.prototype.onClusterEvent = function(evtName, marker) { + var self = this, + mapId = self.pluginMap.__pgmId; + var overlayId = marker.get('overlayId'); + var tmp = overlayId.split('-'); + var clusterId = tmp[0]; + var markerId = tmp[1]; + var latLng = marker.getPosition(); + if (mapId in plugin.google.maps) { + if (self.pluginMap.activeMarker) { + self.onMarkerEvent(event.INFO_CLOSE, self.pluginMap.activeMarker); + } + plugin.google.maps[mapId]({ + 'evtName': evtName, + 'callback': '_onClusterEvent', + 'args': [clusterId, markerId, new LatLng(latLng.lat(), latLng.lng())] + }); + } +}; + + +module.exports = PluginMarkerCluster; + +function newClusterIcon(options) { + + // https://github.com/apache/cordova-js/blob/c75e8059114255d1dbae1ede398e6626708ee9f3/src/common/utils.js#L167 + if (ClusterIconClass.__super__ !== google.maps.OverlayView.prototype) { + var key, defined = {}; + for (key in ClusterIconClass.prototype) { + defined[key] = ClusterIconClass.prototype[key]; + } + utils.extend(ClusterIconClass, google.maps.OverlayView); + for (key in defined) { + ClusterIconClass.prototype[key] = defined[key]; + } + } + + return new ClusterIconClass(options); +} +function ClusterIconClass(options) { + google.maps.OverlayView.apply(this); + var self = this; + //----------------------------------------- + // Create a canvas to draw label + //----------------------------------------- + var canvas = document.createElement('canvas'); + canvas.width = 50; + canvas.height = 50; + canvas.style.visibility = 'hidden'; + canvas.style.position = 'absolute'; + canvas.style.zIndex = -100; + canvas.setAttribute('id', 'canvas_' + options.overlayId); + self.set('canvas', canvas); + + //----------------------------------------- + // Create two markers for icon and label + //----------------------------------------- + var iconMarker = new google.maps.Marker({ + 'clickable': false, + 'icon': options.icon, + 'zIndex': 0, + 'opacity': 0 + }); + var labelMarker = new google.maps.Marker({ + 'clickable': true, + 'zIndex': 1, + 'icon': self.get('label'), + 'opacity': 0 + }); + labelMarker.addListener('click', function() { + google.maps.event.trigger(self, 'click'); + }); + self.set('iconMarker', iconMarker); + self.set('labelMarker', labelMarker); + self.set('opacity', 0); + + iconMarker.bindTo('opacity', labelMarker); + iconMarker.bindTo('visible', labelMarker); + iconMarker.bindTo('position', labelMarker); + iconMarker.bindTo('map', labelMarker); + self.bindTo('opacity', iconMarker); + self.bindTo('visible', iconMarker); + self.bindTo('map', iconMarker); + self.bindTo('position', iconMarker); + self.set('labelMarkerAnchor', new google.maps.Point(canvas.width / 2, canvas.height / 2)); + + self.addListener('icon_changed', function() { + var icon = self.get('icon'); + if (typeof icon === 'string') { + icon = { + 'url': icon + }; + } + + var iconUrl = icon.url; + if (typeof icon === 'object') { + if (typeof icon.size === 'object' && + icon.size.width && icon.size.height) { + icon.anchor = new google.maps.Point(icon.size.width / 2, icon.size.height / 2); + iconMarker.setIcon(icon); + return; + } + } + var img = new Image(); + img.onload = function() { + icon.size = new google.maps.Size(img.width, img.height); + icon.scaledSize = new google.maps.Size(img.width, img.height); + icon.anchor = new google.maps.Point(img.width / 2, img.height / 2); + self.set('labelMarkerAnchor', new google.maps.Point(img.width / 2, img.height / 2)); + iconMarker.setIcon(icon); + }; + img.onerror = function(e) { + console.error(e); + }; + img.src = iconUrl; + }); + + //debug + //var positionConfirmMarker = new google.maps.Marker(); + //labelMarker.bindTo('position', positionConfirmMarker); + //labelMarker.bindTo('map', positionConfirmMarker); + + for (var key in options) { + self.set(key, options[key]); + } + +} + +ClusterIconClass.prototype.onAdd = function() { + var self = this; + var canvas = self.get('canvas'); + self.get('map').getDiv().appendChild(canvas); +}; +ClusterIconClass.prototype.onRemove = function() { + var self = this; + var canvas = self.get('canvas'); + self.set('map', null); + google.maps.event.clearInstanceListeners(self.get('iconMarker')); + google.maps.event.clearInstanceListeners(self.get('labelMarker')); + var parent = canvas.parentNode; + if (parent) { + parent.removeChild(canvas); + } + canvas = undefined; +}; +ClusterIconClass.prototype.draw = function() { + var self = this, + icon = self.get('icon'); + if (typeof icon === 'string') { + icon = { + 'url': icon + }; + } + icon.label = icon.label || {}; + if (self.get('prevText') === icon.label.text) { + return; + } + self.set('prevText', icon.label.text); + self.get('labelMarker').set('opacity', 0); + + (new Promise(function(resolve) { + var iconUrl = icon.url; + if (typeof icon === 'object') { + if (typeof icon.size === 'object' && + icon.size.width && icon.size.height) { + return resolve(icon.size); + } + } + var img = new Image(); + img.onload = function() { + var newIconInfo = { + width: img.width, + height: img.height + }; + icon.size = newIconInfo; + self.set('labelMarkerAnchor', new google.maps.Point(newIconInfo.width / 2, newIconInfo.height / 2)); + self.set('icon', icon, true); + resolve(newIconInfo); + }; + img.onerror = function() { + var newIconInfo = { + width: img.width, + height: img.height + }; + icon.size = newIconInfo; + self.set('icon', icon, true); + resolve(newIconInfo); + }; + img.src = iconUrl; + })) + .then(function(iconSize) { + var canvas = self.get('canvas'), + ctx = canvas.getContext('2d'); + canvas.width = iconSize.width; + canvas.height = iconSize.height; + + var labelOptions = icon.label || { + fontSize: 10, + bold: false, + italic: false + }; + ctx.clearRect(0, 0, canvas.width, canvas.height); + // debug + //ctx.fillStyle="#FF000077"; + //ctx.fillRect(0, 0, canvas.width, canvas.height); + + if (labelOptions.text) { + var fontStyles = []; + + if ('color' in labelOptions) { + ctx.fillStyle = [ + 'rgba(', + labelOptions.color[0], + ',', + labelOptions.color[1], + ',', + labelOptions.color[2], + ',', + labelOptions.color[3] / 255, + ')'].join(''); + } else { + ctx.fillStyle = 'black'; + } + + if (labelOptions.italic === true) { + fontStyles.push('italic'); + } + if (labelOptions.bold === true) { + fontStyles.push('bold'); + } + + fontStyles.push(parseInt(labelOptions.fontSize || '10', 10) + 'px'); + + fontStyles.push('Arial'); + + ctx.font = fontStyles.join(' '); + ctx.textBaseline = 'middle'; + ctx.textAlign = 'center'; + ctx.fillText(labelOptions.text, iconSize.width / 2, iconSize.height / 2); + // debug + //ctx.fillText(selfId.split("-")[1], iconSize.width / 2, iconSize.height / 2); + + } + + self.get('labelMarker').set('icon', { + 'url': canvas.toDataURL(), + 'anchor': self.get('labelMarkerAnchor') + }); + setTimeout(function() { + self.set('opacity', 1); + }, 10); + }); + + +}; +ClusterIconClass.prototype.setIcon = function(icon) { + var self = this; + self.set('icon', icon); +}; +ClusterIconClass.prototype.setVisible = function(visible) { + var self = this; + self.set('visible', visible); +}; +ClusterIconClass.prototype.setPosition = function(position) { + var self = this; + self.set('position', position); +}; + +ClusterIconClass.prototype.getPosition = function() { + var self = this; + return self.get('position'); +}; + +ClusterIconClass.prototype.setMap = function(map) { + var self = this; + self.set('map', map); +}; diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginPolygon.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginPolygon.js new file mode 100644 index 0000000..0be8f84 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginPolygon.js @@ -0,0 +1,371 @@ +var utils = require('cordova/utils'), + event = require('cordova-plugin-googlemaps.event'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'); + +function PluginPolygon(pluginMap) { + var self = this; + BaseClass.apply(self); + Object.defineProperty(self, 'pluginMap', { + value: pluginMap, + writable: false + }); +} + +utils.extend(PluginPolygon, BaseClass); + +PluginPolygon.prototype._create = function(onSuccess, onError, args) { + var self = this, + map = self.pluginMap.get('map'), + polygonId = 'polygon_' + args[2], + pluginOptions = args[1]; + + var polygonOpts = { + 'overlayId': polygonId, + 'map': map, + 'paths': new google.maps.MVCArray() + }; + + if (pluginOptions.points) { + var strokePath = new google.maps.MVCArray(); + pluginOptions.points.forEach(function(point) { + strokePath.push(new google.maps.LatLng(point.lat, point.lng)); + }); + polygonOpts.paths.push(strokePath); + + + if (Array.isArray(pluginOptions.holes)) { + pluginOptions.holes.forEach(function(hole) { + var holeMvc = new google.maps.MVCArray(); + hole.forEach(function(vertix) { + holeMvc.push(new google.maps.LatLng(vertix.lat, vertix.lng)); + }); + polygonOpts.paths.push(holeMvc); + }); + } + } + if (Array.isArray(pluginOptions.strokeColor)) { + polygonOpts.strokeColor = 'rgb(' + pluginOptions.strokeColor[0] + ',' + pluginOptions.strokeColor[1] + ',' + pluginOptions.strokeColor[2] + ')'; + polygonOpts.strokeOpacity = pluginOptions.strokeColor[3] / 256; + } + if (Array.isArray(pluginOptions.fillColor)) { + polygonOpts.fillColor = 'rgb(' + pluginOptions.fillColor[0] + ',' + pluginOptions.fillColor[1] + ',' + pluginOptions.fillColor[2] + ')'; + polygonOpts.fillOpacity = pluginOptions.fillColor[3] / 256; + } + if ('width' in pluginOptions) { + polygonOpts.strokeWeight = pluginOptions.width; + } + if ('zIndex' in pluginOptions) { + polygonOpts.zIndex = pluginOptions.zIndex; + } + if ('visible' in pluginOptions) { + polygonOpts.visible = pluginOptions.visible; + } + if ('geodesic' in pluginOptions) { + polygonOpts.geodesic = pluginOptions.geodesic; + } + if ('clickable' in pluginOptions) { + polygonOpts.clickable = pluginOptions.clickable; + } + + var polygon = new google.maps.Polygon(polygonOpts); + polygon.addListener('click', function(polyMouseEvt) { + self._onPolygonEvent.call(self, polygon, polyMouseEvt); + }); + + self.pluginMap.objects[polygonId] = polygon; + + onSuccess({ + '__pgmId': polygonId + }); +}; + +PluginPolygon.prototype.setFillColor = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var fillColor = args[1]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + + if (Array.isArray(fillColor)) { + polygon.setOptions({ + 'fillColor': 'rgb(' + fillColor[0] + ',' + fillColor[1] + ',' + fillColor[2] + ')', + 'fillOpacity': fillColor[3] / 256 + }); + } + } + onSuccess(); +}; + +PluginPolygon.prototype.setStrokeColor = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polygon = self.pluginMap.objects[overlayId]; + var strokeColor = args[1]; + if (polygon) { + if (Array.isArray(strokeColor)) { + polygon.setOptions({ + 'strokeColor': 'rgb(' + strokeColor[0] + ',' + strokeColor[1] + ',' + strokeColor[2] + ')', + 'strokeOpacity': strokeColor[3] / 256 + }); + } + } + onSuccess(); +}; + +PluginPolygon.prototype.setStrokeWidth = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var width = args[1]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + polygon.setOptions({ + 'strokeWeight': width + }); + } + onSuccess(); +}; + + +PluginPolygon.prototype.setZIndex = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var zIndex = args[1]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + polygon.setOptions({ + 'zIndex': zIndex + }); + } + onSuccess(); +}; + +PluginPolygon.prototype.setPoints = function(onSuccess, onError, args) { + var self = this, + polygonId = args[0], + positionList = args[1], + polygon = self.pluginMap.objects[polygonId]; + if (polygon) { + //------------------------ + // Update the points list + //------------------------ + polygon.setPath(positionList); + } + onSuccess(); +}; + +PluginPolygon.prototype.setClickable = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var clickable = args[1]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + polygon.setOptions({ + 'clickable': clickable === true + }); + } + onSuccess(); +}; + +PluginPolygon.prototype.setVisible = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + polygon.setVisible(args[1]); + } + onSuccess(); +}; + +PluginPolygon.prototype.setGeodesic = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + polygon.setOptions({ + 'geodesic': args[1] === true + }); + } + onSuccess(); +}; +PluginPolygon.prototype.insertPointAt = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + var index = args[1]; + var latLng = new google.maps.LatLng(args[2].lat, args[2].lng); + polygon.getPath().insertAt(index, latLng); + } + onSuccess(); +}; +PluginPolygon.prototype.setPointAt = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + var index = args[1]; + var latLng = new google.maps.LatLng(args[2].lat, args[2].lng); + polygon.getPath().setAt(index, latLng); + } + onSuccess(); +}; +PluginPolygon.prototype.removePointAt = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + var index = args[1]; + polygon.getPath().removeAt(index); + } + onSuccess(); +}; + +PluginPolygon.prototype.setHoles = function(onSuccess, onError, args) { + var self = this, + overlayId = args[0], + polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + var paths = polygon.getPaths(), + holeList = args[1], + newPaths = new google.maps.MVCArray([paths.getAt(0)]); + + holeList.forEach(function(holeVertixes) { + var hole = new google.maps.MVCArray(); + holeVertixes.forEach(function(vertix) { + hole.push(new google.maps.LatLng(vertix.lat, vertix.lng)); + }); + newPaths.push(hole); + }); + polygon.setPaths(newPaths); + } + onSuccess(); +}; + +PluginPolygon.prototype.insertPointOfHoleAt = function(onSuccess, onError, args) { + var self = this, + overlayId = args[0], + holeIndex = args[1] + 1, // idx=0 is for outter vertixes + pointIndex = args[2], + position = args[3], + polygon = self.pluginMap.objects[overlayId]; + + if (polygon) { + var latLng = new google.maps.LatLng(position.lat, position.lng); + var paths = polygon.getPaths(); + var hole = null; + if (holeIndex < paths.getLength()) { + hole = paths.getAt(holeIndex); + } + if (!hole) { + hole = new google.maps.MVCArray(); + paths.push(hole); + } + hole.insertAt(pointIndex, latLng); + } + onSuccess(); +}; + +PluginPolygon.prototype.setPointOfHoleAt = function(onSuccess, onError, args) { + var self = this, + overlayId = args[0], + holeIndex = args[1] + 1, + pointIndex = args[2], + position = args[3], + polygon = self.pluginMap.objects[overlayId]; + + if (polygon) { + var latLng = new google.maps.LatLng(position.lat, position.lng); + polygon.getPaths().getAt(holeIndex).setAt(pointIndex, latLng); + } + onSuccess(); +}; + +PluginPolygon.prototype.removePointOfHoleAt = function(onSuccess, onError, args) { + var self = this, + overlayId = args[0], + holeIndex = args[1] + 1, + pointIndex = args[2], + polygon = self.pluginMap.objects[overlayId]; + + if (polygon) { + polygon.getPaths().getAt(holeIndex).removeAt(pointIndex); + } + onSuccess(); +}; + + +PluginPolygon.prototype.insertHoleAt = function(onSuccess, onError, args) { + var self = this, + overlayId = args[0], + holeIndex = args[1] + 1, + vertixes = args[2], + polygon = self.pluginMap.objects[overlayId]; + + if (polygon) { + var hole = new google.maps.MVCArray(); + vertixes.forEach(function(vertix) { + hole.push(new google.maps.LatLng(vertix.lat, vertix.lng)); + }); + polygon.getPaths().insertAt(holeIndex, hole); + } + onSuccess(); +}; + + + +PluginPolygon.prototype.setHoleAt = function(onSuccess, onError, args) { + var self = this, + overlayId = args[0], + holeIndex = args[1] + 1, + vertixes = args[2], + polygon = self.pluginMap.objects[overlayId]; + + if (polygon) { + var hole = new google.maps.MVCArray(); + vertixes.forEach(function(vertix) { + hole.push(new google.maps.LatLng(vertix.lat, vertix.lng)); + }); + polygon.getPaths().setAt(holeIndex, hole); + } + onSuccess(); +}; + + +PluginPolygon.prototype.removeHoleAt = function(onSuccess, onError, args) { + var self = this, + overlayId = args[0], + holeIndex = args[1] + 1, + polygon = self.pluginMap.objects[overlayId]; + + if (polygon) { + polygon.getPaths().removeAt(holeIndex); + } + onSuccess(); +}; +PluginPolygon.prototype.remove = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polygon = self.pluginMap.objects[overlayId]; + if (polygon) { + google.maps.event.clearInstanceListeners(polygon); + polygon.setMap(null); + polygon = undefined; + self.pluginMap.objects[overlayId] = undefined; + delete self.pluginMap.objects[overlayId]; + } + onSuccess(); +}; + +PluginPolygon.prototype._onPolygonEvent = function(polygon, polyMouseEvt) { + var self = this, + mapId = self.pluginMap.__pgmId; + if (mapId in plugin.google.maps) { + plugin.google.maps[mapId]({ + 'evtName': event.POLYGON_CLICK, + 'callback': '_onOverlayEvent', + 'args': [polygon.overlayId, new plugin.google.maps.LatLng(polyMouseEvt.latLng.lat(), polyMouseEvt.latLng.lng())] + }); + } + +}; +module.exports = PluginPolygon; diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginPolyline.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginPolyline.js new file mode 100644 index 0000000..3a64249 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginPolyline.js @@ -0,0 +1,216 @@ + + +var utils = require('cordova/utils'), + event = require('cordova-plugin-googlemaps.event'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'); + +function PluginPolyline(pluginMap) { + var self = this; + BaseClass.apply(self); + Object.defineProperty(self, 'pluginMap', { + value: pluginMap, + writable: false + }); +} + +utils.extend(PluginPolyline, BaseClass); + +PluginPolyline.prototype._create = function(onSuccess, onError, args) { + var self = this, + map = self.pluginMap.get('map'), + polylineId = 'polyline_' + args[2], + pluginOptions = args[1]; + + var polylineOpts = { + 'overlayId': polylineId, + 'map': map, + 'path': new google.maps.MVCArray() + }; + + if (pluginOptions.points) { + pluginOptions.points.forEach(function(point) { + polylineOpts.path.push(new google.maps.LatLng(point.lat, point.lng)); + }); + } + if (Array.isArray(pluginOptions.color)) { + polylineOpts.strokeColor = 'rgb(' + pluginOptions.color[0] + ',' + pluginOptions.color[1] + ',' + pluginOptions.color[2] + ')'; + polylineOpts.strokeOpacity = pluginOptions.color[3] / 256; + } + if ('width' in pluginOptions) { + polylineOpts.strokeWeight = pluginOptions.width; + } + if ('zIndex' in pluginOptions) { + polylineOpts.zIndex = pluginOptions.zIndex; + } + if ('visible' in pluginOptions) { + polylineOpts.visible = pluginOptions.visible; + } + if ('geodesic' in pluginOptions) { + polylineOpts.geodesic = pluginOptions.geodesic; + } + if ('clickable' in pluginOptions) { + polylineOpts.clickable = pluginOptions.clickable; + } + + var polyline = new google.maps.Polyline(polylineOpts); + polyline.addListener('click', function(polyMouseEvt) { + self._onPolylineEvent.call(self, polyline, polyMouseEvt); + }); + + self.pluginMap.objects[polylineId] = polyline; + + onSuccess({ + '__pgmId': polylineId + }); +}; +PluginPolyline.prototype.remove = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + google.maps.event.clearInstanceListeners(polyline); + polyline.setMap(null); + polyline = undefined; + self.pluginMap.objects[overlayId] = undefined; + delete self.pluginMap.objects[overlayId]; + } + onSuccess(); +}; + +PluginPolyline.prototype.setStrokeColor = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polyline = self.pluginMap.objects[overlayId]; + var strokeColor = args[1]; + if (polyline) { + if (Array.isArray(strokeColor)) { + polyline.setOptions({ + 'strokeColor': 'rgb(' + strokeColor[0] + ',' + strokeColor[1] + ',' + strokeColor[2] + ')', + 'strokeOpacity': strokeColor[3] / 256 + }); + } + } + onSuccess(); +}; + +PluginPolyline.prototype.setStrokeWidth = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var width = args[1]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + polyline.setOptions({ + 'strokeWeight': width + }); + } + onSuccess(); +}; + + +PluginPolyline.prototype.setZIndex = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var zIndex = args[1]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + polyline.setOptions({ + 'zIndex': zIndex + }); + } + onSuccess(); +}; + +PluginPolyline.prototype.setPoints = function(onSuccess, onError, args) { + var self = this, + polylineId = args[0], + positionList = args[1], + polyline = self.pluginMap.objects[polylineId]; + if (polyline) { + //------------------------ + // Update the points list + //------------------------ + polyline.setPath(positionList); + } + onSuccess(); +}; + +PluginPolyline.prototype.setClickable = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var clickable = args[1]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + polyline.setOptions({ + 'clickable': clickable === true + }); + } + onSuccess(); +}; + +PluginPolyline.prototype.setVisible = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + polyline.setVisible(args[1]); + } + onSuccess(); +}; + +PluginPolyline.prototype.setGeodesic = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + polyline.setOptions({ + 'geodesic': args[1] === true + }); + } + onSuccess(); +}; +PluginPolyline.prototype.insertPointAt = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + var index = args[1]; + var latLng = new google.maps.LatLng(args[2].lat, args[2].lng); + polyline.getPath().insertAt(index, latLng); + } + onSuccess(); +}; +PluginPolyline.prototype.setPointAt = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + var index = args[1]; + var latLng = new google.maps.LatLng(args[2].lat, args[2].lng); + polyline.getPath().setAt(index, latLng); + } + onSuccess(); +}; +PluginPolyline.prototype.removePointAt = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var polyline = self.pluginMap.objects[overlayId]; + if (polyline) { + var index = args[1]; + polyline.getPath().removeAt(index); + } + onSuccess(); +}; + +PluginPolyline.prototype._onPolylineEvent = function(polyline, polyMouseEvt) { + var self = this, + mapId = self.pluginMap.__pgmId; + if (mapId in plugin.google.maps) { + plugin.google.maps[mapId]({ + 'evtName': event.POLYLINE_CLICK, + 'callback': '_onOverlayEvent', + 'args': [polyline.overlayId, new plugin.google.maps.LatLng(polyMouseEvt.latLng.lat(), polyMouseEvt.latLng.lng())] + }); + } + +}; +module.exports = PluginPolyline; diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginStreetViewPanorama.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginStreetViewPanorama.js new file mode 100644 index 0000000..c5d4535 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginStreetViewPanorama.js @@ -0,0 +1,346 @@ + +var utils = require('cordova/utils'), + event = require('cordova-plugin-googlemaps.event'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'); + +function displayGrayMap(container) { + var gmErrorContent = document.querySelector('.gm-err-container'); + var gmnoprint = document.querySelector('.gmnoprint'); + if (!gmErrorContent && !gmnoprint) { + container.innerHTML = [ + '
', + '
', + '
', + '

Can not display panorama.
Check the developer console.

', + '
', + '
' + ].join('\n'); + } +} + +function PluginStreetViewPanorama(panoramaId, options) { + var self = this; + BaseClass.apply(this); + var panoramaDiv = document.querySelector('[__pluginMapId=\'' + panoramaId + '\']'); + panoramaDiv.style.backgroundColor = 'rgb(229, 227, 223)'; + var container = document.createElement('div'); + container.style.userSelect='none'; + container.style['-webkit-user-select']='none'; + container.style['-moz-user-select']='none'; + container.style['-ms-user-select']='none'; + panoramaDiv.style.position = 'relative'; + container.style.position = 'absolute'; + container.style.top = 0; + container.style.bottom = 0; + container.style.right = 0; + container.style.left = 0; + container.style.zIndex = 0; + panoramaDiv.insertBefore(container, panoramaDiv.firstElementChild); + + self.set('isGoogleReady', false); + self.set('container', container); + self.PLUGINS = {}; + + Object.defineProperty(self, '__pgmId', { + value: panoramaId, + writable: false + }); + + self.one('googleready', function() { + self.set('isGoogleReady', true); + + var service = new google.maps.StreetViewService(); + self.set('service', service); + new Promise(function(resolve, reject) { + if (options.camera) { + var request = {}; + if (typeof options.camera.target === 'string') { + request.pano = options.camera.target; + } else { + request.location = options.camera.target; + request.radius = options.camera.radius | 50; + request.source = options.camera.source === 'OUTDOOR' ? + google.maps.StreetViewSource.OUTDOOR : google.maps.StreetViewSource.DEFAULT; + } + var timeoutError = setTimeout(function() { + self.trigger('load_error'); + displayGrayMap(panoramaDiv); + reject(); + }, 3000); + + service.getPanorama(request, function(data, status) { + clearTimeout(timeoutError); + if (status === google.maps.StreetViewStatus.OK) { + resolve(data.location.pano); + } else { + resolve(null); + } + }); + + } else { + resolve(null); + } + }) + .then(function(panoId) { + + var stOptions = { + 'addressControl': options.controls.streetNames, + 'showRoadLabels': options.controls.streetNames, + 'linksControl': options.controls.navigation, + 'panControl': options.gestures.panning, + 'zoomControl': options.gestures.zoom, + 'scrollwheel': options.gestures.zoom, + 'pano': panoId + }; + if (options.camera) { + if ('zoom' in options.camera) { + stOptions.zoom = options.camera.zoom; + } + var pov; + if ('tilt' in options.camera) { + pov = {}; + pov.pitch = options.camera.tilt; + } + if ('bearing' in options.camera) { + pov = pov || {}; + pov.heading = options.camera.bearing; + } + if (pov) { + stOptions.pov = pov; + } + } + + google.maps.event.addDomListener(container, 'click', function(evt) { + var pov = panorama.getPov(); + var clickInfo = { + 'orientation': { + 'bearing': pov.heading, + 'tilt': pov.pitch + }, + 'point': [evt.clientX, evt.clientY] + }; + if (self.__pgmId in plugin.google.maps) { + plugin.google.maps[self.__pgmId]({ + 'evtName': event.PANORAMA_CLICK, + 'callback': '_onPanoramaEvent', + 'args': [clickInfo] + }); + } + }); + var panorama = new google.maps.StreetViewPanorama(container, stOptions); + self.set('panorama', panorama); + + self.trigger(event.PANORAMA_READY); + panorama.addListener('position_changed', self._onPanoChangedEvent.bind(self, panorama)); + panorama.addListener('pov_changed', self._onCameraEvent.bind(self, panorama)); + panorama.addListener('zoom_changed', self._onCameraEvent.bind(self, panorama)); + + if (!panoId) { + self._onPanoChangedEvent(null); + } + }); + + + }); +} + +utils.extend(PluginStreetViewPanorama, BaseClass); + +PluginStreetViewPanorama.prototype.setPanningGesturesEnabled = function(onSuccess, onError, args) { + var self = this; + var panorama = self.get('panorama'); + var boolValue = args[0] === true; + + if (panorama) { + panorama.setOptions({ + 'panControl': boolValue + }); + } + + onSuccess(); +}; + +PluginStreetViewPanorama.prototype.setZoomGesturesEnabled = function(onSuccess, onError, args) { + var self = this; + var panorama = self.get('panorama'); + var boolValue = args[0] === true; + + if (panorama) { + panorama.setOptions({ + 'zoomControl': boolValue, + 'scrollwheel': boolValue, + }); + } + + onSuccess(); +}; + + +PluginStreetViewPanorama.prototype.setNavigationEnabled = function(onSuccess, onError, args) { + var self = this; + var panorama = self.get('panorama'); + var boolValue = args[0] === true; + + if (panorama) { + panorama.setOptions({ + 'linksControl': boolValue + }); + } + + onSuccess(); +}; + +PluginStreetViewPanorama.prototype.setStreetNamesEnabled = function(onSuccess, onError, args) { + var self = this; + var panorama = self.get('panorama'); + var boolValue = args[0] === true; + + if (panorama) { + panorama.setOptions({ + 'addressControl': boolValue, + 'showRoadLabels': boolValue + }); + } + + onSuccess(); +}; +PluginStreetViewPanorama.prototype.setStreetNamesEnabled = function(onSuccess, onError, args) { + var self = this; + var panorama = self.get('panorama'); + var boolValue = args[0] === true; + + if (panorama) { + panorama.setOptions({ + 'visible': boolValue + }); + } + + onSuccess(); +}; + +PluginStreetViewPanorama.prototype.setPosition = function(onSuccess, onError, args) { + var self = this; + var panorama = self.get('panorama'); + var camera = args[0]; + + if (panorama) { + var service = self.get('service'); + + new Promise(function(resolve, reject) { + var request = {}; + if (typeof camera.target === 'string') { + request.pano = camera.target; + } else { + request.location = camera.target; + request.radius = camera.radius | 50; + request.source = camera.source === 'OUTDOOR' ? + google.maps.StreetViewSource.OUTDOOR : google.maps.StreetViewSource.DEFAULT; + } + var timeoutError = setTimeout(function() { + reject('timeout error'); + }, 3000); + + service.getPanorama(request, function(data, status) { + clearTimeout(timeoutError); + if (status === google.maps.StreetViewStatus.OK) { + resolve(data.location.pano); + } else { + resolve(null); + } + }); + }) + .then(function(panoId) { + panorama.setPano(panoId); + if (!panoId) { + self._onPanoChangedEvent(null); + } + onSuccess(); + }) + .catch(onError); + } else { + onError('panorama has been already removed.'); + } + +}; + +PluginStreetViewPanorama.prototype.setPov = function(onSuccess, onError, args) { + var self = this; + var panorama = self.get('panorama'); + var povRequest = args[0]; + + if (panorama) { + var options = { + pov: panorama.getPov() + }; + options.pov = options.pov || { + heading: 0, + pitch: 0 + }; + + if ('bearing' in povRequest) { + options.pov.heading = povRequest.bearing; + } + if ('tilt' in povRequest) { + options.pov.pitch = povRequest.tilt; + } + if ('zoom' in povRequest) { + options.zoom = povRequest.zoom; + } + panorama.setOptions(options); + } + + onSuccess(); +}; + + +PluginStreetViewPanorama.prototype._onCameraEvent = function(panorama) { + var self = this; + var pov = panorama.getPov(); + var camera = { + 'bearing': pov.heading, + 'tilt': pov.pitch, + 'zoom': panorama.getZoom() + }; + if (self.__pgmId in plugin.google.maps) { + plugin.google.maps[self.__pgmId]({ + 'evtName': event.PANORAMA_CAMERA_CHANGE, + 'callback': '_onPanoramaCameraChange', + 'args': [camera] + }); + } +}; +PluginStreetViewPanorama.prototype._onPanoChangedEvent = function(panorama) { + var self = this; + var locationInfo = null; + + if (panorama) { + var location = panorama.getLocation(); + + locationInfo = { + 'panoId': location.pano, + 'latLng': { + 'lat': location.latLng.lat(), + 'lng': location.latLng.lng() + } + }; + + var links = panorama.getLinks() || []; + if (links) { + locationInfo.links = links.map(function(link) { + return { + 'panoId': link.pano, + 'bearing': link.heading + }; + }); + } + } + if (self.__pgmId in plugin.google.maps) { + plugin.google.maps[self.__pgmId]({ + 'evtName': event.PANORAMA_LOCATION_CHANGE, + 'callback': '_onPanoramaLocationChange', + 'args': [locationInfo] + }); + } +}; + +module.exports = PluginStreetViewPanorama; diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/PluginTileOverlay.js b/node_modules/cordova-plugin-googlemaps/src/browser/PluginTileOverlay.js new file mode 100644 index 0000000..48c08dc --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/PluginTileOverlay.js @@ -0,0 +1,299 @@ + +var utils = require('cordova/utils'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'); + +function TileOverlay(mapId, hashCode, options) { + var tileSize = 256, + tileCaches = {}, + _opacity = 'opacity' in options ? options.opacity : 1; + + var mapType = { + zIndex: options.zIndex || 0, + name: options.name || hashCode, + mapTypeId: hashCode, + fadeIn: options.fadeIn === false ? false : true, + visible: true, + + setOpacity: function(opacity) { + _opacity = opacity; + var keys = Object.keys(tileCaches); + keys.forEach(function(key) { + tileCaches[key].style.opacity = _opacity; + }); + }, + getTileFromCache: function(cacheId) { + return tileCaches[cacheId]; + }, + getZIndex: function() { + return this.zIndex; + }, + setZIndex: function(zIndexVal) { + this.zIndex = parseInt(zIndexVal, 10); + }, + + getOpacity: function() { + return _opacity; + }, + + tileSize: new google.maps.Size(tileSize, tileSize), + + setVisible: function(visible) { + this.visible = visible; + var visibility = visible ? 'visible': 'hidden'; + var keys = Object.keys(tileCaches); + keys.forEach(function(key) { + tileCaches[key].style.visibility = visibility; + }); + }, + + getTile: function(coord, zoom) { + + var cacheId = [mapId, hashCode, coord.x, coord.y, zoom].join('-'); + if (cacheId in tileCaches) { + return tileCaches[cacheId]; + } else { + var div = document.createElement('div'); + div.style.width = tileSize + 'px'; + div.style.height = tileSize + 'px'; + div.style.opacity = 0; + div.style.visibility = 'hidden'; + + if (options.debug) { + div.style.borderLeft = '1px solid red'; + div.style.borderTop = '1px solid red'; + div.style.color = 'red'; + div.style.fontSize = '12px'; + div.style.padding = '1em'; + div.innerHTML = 'x = ' + coord.x + ', y = ' + coord.y + ', zoom = ' + zoom; + } + + div.setAttribute('cacheId', cacheId); + tileCaches[cacheId] = div; + + options.getTile(coord.x, coord.y, zoom, cacheId); + return div; + } + }, + + releaseTile: function(div) { + var cacheId = div.getAttribute('cacheId'); + delete tileCaches[cacheId]; + }, + + remove: function() { + var keys = Object.keys(tileCaches); + keys.forEach(function(key) { + if (tileCaches[key].parentNode) { + tileCaches[key].parentNode.removeChild(tileCaches[key]); + } + tileCaches[key] = undefined; + delete tileCaches[key]; + }); + } + }; + + return mapType; +} + + +function PluginTileOverlay(pluginMap) { + var self = this; + BaseClass.apply(self); + Object.defineProperty(self, 'pluginMap', { + value: pluginMap, + enumerable: false, + writable: false + }); + +} + +utils.extend(PluginTileOverlay, BaseClass); + +PluginTileOverlay.prototype._create = function(onSuccess, onError, args) { + var self = this, + map = self.pluginMap.get('map'), + hashCode = args[2], + tileoverlayId = 'tileoverlay_' + hashCode, + pluginOptions = args[1], + mapId = self.pluginMap.__pgmId, + getTileEventName = mapId + '-' + hashCode + '-tileoverlay'; + + pluginOptions.getTile = function(x, y, zoom, urlCallbackId) { + cordova.fireDocumentEvent(getTileEventName, { + key: urlCallbackId, + x: x, + y: y, + zoom: zoom + }); + }; + + var tileoverlay = new TileOverlay(self.pluginMap.__pgmId, hashCode, pluginOptions); + + var layers = map.overlayMapTypes.getArray(); + layers = layers.filter(function(layer) { + return !!layer; + }); + layers.forEach(function(layer, idx) { + if (!layer) { + return; + } + if (layer.zIndex === undefined) { + layer.zIndex = idx; + } + if (!layer.getZIndex) { + layer.getZIndex = function() { + return layer.zIndex; + }; + } + if (!layer.setZIndex) { + layer.setZIndex = function(zIndexVal) { + layer.zIndex = zIndexVal; + }; + } + }); + layers.push(tileoverlay); + layers = layers.sort(function(a, b) { + return a.getZIndex() - b.getZIndex(); + }); + layers.forEach(function(layer, idx) { + if (idx < map.overlayMapTypes.getLength()) { + layer.zIndex = idx; + map.overlayMapTypes.setAt(idx, layer); + } else { + layer.zIndex = idx; + map.overlayMapTypes.push(layer); + } + }); + + self.pluginMap.objects[tileoverlayId] = tileoverlay; + + onSuccess({ + '__pgmId': tileoverlayId + }); +}; + + +PluginTileOverlay.prototype.onGetTileUrlFromJS = function(onSuccess, onError, args) { + + var self = this, + cacheId = args[1], + tileUrl = args[2]; + + var tmp = cacheId.split(/-/), + hashCode = tmp[1], + tileoverlayId = 'tileoverlay_' + hashCode; + + var tileLayer = self.pluginMap.objects[tileoverlayId]; + + if (tileLayer && tileLayer.getTileFromCache(cacheId)) { + var tile = tileLayer.getTileFromCache(cacheId); + tile.style.backgroundImage = 'url(\'' + tileUrl + '\')'; + tile.style.visibility = tileLayer.visible ? 'visible': 'hidden'; + + if (tileLayer.fadeIn) { + fadeInAnimation(tile, 500, tileLayer.getOpacity()); + } else { + tile.style.opacity = tileLayer.getOpacity(); + } + } + onSuccess(); +}; + +PluginTileOverlay.prototype.setVisible = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var tileoverlay = self.pluginMap.objects[overlayId]; + if (tileoverlay) { + tileoverlay.setVisible(args[1]); + } + onSuccess(); +}; + +PluginTileOverlay.prototype.setOpacity = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var opacity = args[1]; + var tileoverlay = self.pluginMap.objects[overlayId]; + if (tileoverlay) { + tileoverlay.setOpacity(opacity); + } + onSuccess(); +}; +PluginTileOverlay.prototype.setFadeIn = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var tileoverlay = self.pluginMap.objects[overlayId]; + if (tileoverlay) { + tileoverlay.fadeIn = args[1] === false ? false : true; + } + onSuccess(); +}; +PluginTileOverlay.prototype.setZIndex = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var map = self.pluginMap.get('map'); + var tileoverlay = self.pluginMap.objects[overlayId]; + if (tileoverlay) { + tileoverlay.setZIndex(args[1]); + + var layers = map.overlayMapTypes.getArray(); + layers = layers.sort(function(a, b) { + return a.getZIndex() - b.getZIndex(); + }); + layers.forEach(function(layer, idx) { + if (idx < map.overlayMapTypes.getLength()) { + map.overlayMapTypes.setAt(idx, layer); + } else { + map.overlayMapTypes.push(layer); + } + }); + + } + + + onSuccess(); +}; + +PluginTileOverlay.prototype.remove = function(onSuccess, onError, args) { + var self = this; + var overlayId = args[0]; + var tileoverlay = self.pluginMap.objects[overlayId]; + if (tileoverlay) { + google.maps.event.clearInstanceListeners(tileoverlay); + tileoverlay.remove(); + + + var layers = self.pluginMap.get('map').overlayMapTypes.getArray(); + layers.forEach(function(layer, idx) { + if (layer === tileoverlay) { + layers.splice(idx, 1); + } + }); + + + tileoverlay = undefined; + self.pluginMap.objects[overlayId] = undefined; + delete self.pluginMap.objects[overlayId]; + } + onSuccess(); +}; + +module.exports = PluginTileOverlay; + +function fadeInAnimation(el, time, maxOpacity) { + el.style.opacity = 0; + var timeFunc = typeof window.requestAnimationFrame === 'function' ? requestAnimationFrame : setTimeout; + + var last = Date.now(); + var tick = function() { + var now = Date.now(); + el.style.opacity = +el.style.opacity + (now - last) / time; + last = now; + + if (+el.style.opacity < maxOpacity) { + timeFunc.call(window, tick, 16); + } + }; + + tick(); +} diff --git a/node_modules/cordova-plugin-googlemaps/src/browser/__test__/PluginMap.spec.js b/node_modules/cordova-plugin-googlemaps/src/browser/__test__/PluginMap.spec.js new file mode 100644 index 0000000..eb167b1 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/browser/__test__/PluginMap.spec.js @@ -0,0 +1,19 @@ +const PluginMap = require('../PluginMap'); + +describe('PluginMap', () => { + it('should exist', () => { + expect(PluginMap).toBeDefined(); + }); + + describe('toDataURL', () => { + afterEach(() => { + document.body.innerHTML = ''; + }); + + it('should exist', () => { + var mapId = 'map'; + document.body.innerHTML = '
'; + expect(new PluginMap(mapId, {}).toDataURL).toBeDefined(); + }); + }); +}); diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/CordovaGoogleMaps.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/CordovaGoogleMaps.h new file mode 100644 index 0000000..ef83930 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/CordovaGoogleMaps.h @@ -0,0 +1,33 @@ +// +// CordovaGoogleMaps.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import +#import +#import "PluginMapViewController.h" +#import "PluginStreetViewPanoramaController.h" +#import "PluginUtil.h" +#import "PluginMap.h" +#import "PluginStreetViewPanorama.h" +#import "MyPluginLayer.h" +#import "IPluginProtocol.h" +#import "IPluginView.h" + +@interface CordovaGoogleMaps : CDVPlugin + +@property (nonatomic) MyPluginLayer *pluginLayer; +@property (nonatomic) NSMutableDictionary *viewPlugins; +@property (nonatomic) NSOperationQueue *executeQueue; + +- (void)getMap:(CDVInvokedUrlCommand*)command; +- (void)getPanorama:(CDVInvokedUrlCommand*)command; +- (void)clearHtmlElements:(CDVInvokedUrlCommand *)command; +- (void)putHtmlElements:(CDVInvokedUrlCommand *)command; +- (void)removeMap:(CDVInvokedUrlCommand *)command; +- (void)pause:(CDVInvokedUrlCommand *)command; +- (void)resume:(CDVInvokedUrlCommand *)command; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/CordovaGoogleMaps.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/CordovaGoogleMaps.m new file mode 100644 index 0000000..e5e29de --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/CordovaGoogleMaps.m @@ -0,0 +1,538 @@ +// +// CordovaGoogleMaps.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" + +@implementation CordovaGoogleMaps + +- (void)pluginInitialize +{ + + self.webView.backgroundColor = [UIColor clearColor]; + self.webView.opaque = NO; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:nil]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + self.executeQueue = [NSOperationQueue new]; + self.executeQueue.maxConcurrentOperationCount = 10; + }); + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + //------------------------------- + // Check the Google Maps API key + //------------------------------- + NSString *APIKey = [((CDVViewController *)self.viewController).settings objectForKey:@"google_maps_ios_api_key"]; + + if (APIKey == nil) { + NSString *errorTitle = [PluginUtil PGM_LOCALIZATION:@"APIKEY_IS_UNDEFINED_TITLE"]; + NSString *errorMsg = [PluginUtil PGM_LOCALIZATION:@"APIKEY_IS_UNDEFINED_MESSAGE"]; + + UIAlertController* alert = [UIAlertController alertControllerWithTitle:errorTitle + message:errorMsg + preferredStyle:UIAlertControllerStyleAlert]; + + NSString *closeBtnLabel = [PluginUtil PGM_LOCALIZATION:@"CLOSE_BUTTON"]; + UIAlertAction* ok = [UIAlertAction actionWithTitle:closeBtnLabel + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + [self.viewController presentViewController:alert + animated:YES + completion:nil]; + return; + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didRotate:) + name:UIDeviceOrientationDidChangeNotification object:nil]; + + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + + [GMSServices provideAPIKey:APIKey]; + }]; + + //------------------------------- + // Plugin initialization + //------------------------------- + self.viewPlugins = [[NSMutableDictionary alloc] init]; + + self.pluginLayer = [[MyPluginLayer alloc] initWithWebView:self.webView]; + self.pluginLayer.backgroundColor = [UIColor whiteColor]; + self.pluginLayer.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + + NSArray *subViews = self.viewController.view.subviews; + //NSLog(@"--->subViews count=%lu", subViews.count); + UIView *view; + for (int i = 0; i < [subViews count]; i++) { + view = [subViews objectAtIndex:i]; + //NSLog(@"--->remove i=%d class=%@", i, view.class); + [view removeFromSuperview]; + [self.pluginLayer addSubview: view]; + } + [self.viewController.view addSubview:self.pluginLayer]; +} +- (void) didRotate:(id)sender +{ + + NSArray *keys = [self.viewPlugins allKeys]; + NSString *key; + CDVPlugin *viewPlugin; + PluginMap *pluginMap; + for (int i = 0; i < keys.count; i++) { + key = [keys objectAtIndex:i]; + if ([self.viewPlugins objectForKey:key]) { + viewPlugin = [self.viewPlugins objectForKey:key]; + if ([viewPlugin isKindOfClass:[PluginMap class]]) { + pluginMap = (PluginMap *)viewPlugin; + // Trigger the CAMERA_MOVE_END mandatory + [pluginMap.mapCtrl mapView:pluginMap.mapCtrl.map idleAtCameraPosition:pluginMap.mapCtrl.map.camera]; + } + } + } + +} + + +-(void)viewDidLayoutSubviews { + [self.pluginLayer.pluginScrollView setContentSize: self.webView.scrollView.contentSize]; + [self.pluginLayer.pluginScrollView flashScrollIndicators]; +} + +- (void)onReset +{ + [super onReset]; + + // Reset the background color + self.pluginLayer.backgroundColor = [UIColor whiteColor]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + // Remove all url caches + [[NSURLCache sharedURLCache] removeAllCachedResponses]; + + // Remove old plugins that are used in the previous html. + NSString *mapId; + NSArray *keys=[self.viewPlugins allKeys]; + for (int i = 0; i < [keys count]; i++) { + mapId = [keys objectAtIndex:i]; + [self _destroyMap:mapId]; + } + [self.viewPlugins removeAllObjects]; + + @synchronized(self.pluginLayer.pluginScrollView.HTMLNodes) { + [self.pluginLayer.pluginScrollView.HTMLNodes removeAllObjects]; + self.pluginLayer.pluginScrollView.HTMLNodes = nil; + } + [self.pluginLayer.pluginScrollView.mapCtrls removeAllObjects]; + + }); + +} +-(void)pageDidLoad { + self.webView.backgroundColor = [UIColor clearColor]; + self.webView.opaque = NO; + +} + +- (void)_destroyMap:(NSString *)mapId { + if (![self.viewPlugins objectForKey:mapId]) { + return; + } + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + + CDVPlugin *pluginView = [self.viewPlugins objectForKey:mapId]; + if ([mapId hasPrefix:@"streetview_"]) { + PluginStreetViewPanorama *pluginSV = (PluginStreetViewPanorama *)pluginView; + pluginSV.isRemoved = YES; + //[pluginSV clear:nil]; + [pluginSV pluginUnload]; + [cdvViewController.pluginObjects setObject:pluginView forKey:mapId]; + [cdvViewController.pluginsMap setValue:mapId forKey:mapId]; + + [self.pluginLayer removePluginOverlay:pluginSV.panoramaCtrl]; + pluginSV.panoramaCtrl.view = nil; + pluginSV = nil; + } else { + PluginMap *pluginMap = (PluginMap *)pluginView; + pluginMap.isRemoved = YES; + //[pluginMap clear:nil]; + [pluginMap pluginUnload]; + + [cdvViewController.pluginObjects setObject:pluginView forKey:mapId]; + [cdvViewController.pluginsMap setValue:mapId forKey:mapId]; + + [self.pluginLayer removePluginOverlay:pluginMap.mapCtrl]; + + pluginMap.mapCtrl.view = nil; + [pluginMap.mapCtrl.plugins removeAllObjects]; + pluginMap.mapCtrl.plugins = nil; + pluginMap.mapCtrl.view = nil; + pluginMap.mapCtrl = nil; + pluginMap = nil; + } + + + [self.viewPlugins removeObjectForKey:mapId]; + + [cdvViewController.pluginObjects removeObjectForKey:mapId]; +} +/** + * Remove the map + */ +- (void)removeMap:(CDVInvokedUrlCommand *)command { + NSString *mapId = [command.arguments objectAtIndex:0]; + [self _destroyMap:mapId]; + + if (command != nil) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + +} + +/** + * Intialize the map + */ +- (void)getMap:(CDVInvokedUrlCommand *)command { + if (self.pluginLayer != nil) { + self.pluginLayer.isSuspended = false; + } + + + dispatch_async(dispatch_get_main_queue(), ^{ + + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + NSDictionary *meta = [command.arguments objectAtIndex:0]; + NSString *mapId = [meta objectForKey:@"__pgmId"]; + NSDictionary *initOptions = [command.arguments objectAtIndex:1]; + + // Wrapper view + PluginMapViewController* viewCtrl = [[PluginMapViewController alloc] initWithOptions:nil]; + viewCtrl.webView = self.webView; + viewCtrl.isFullScreen = YES; + viewCtrl.overlayId = mapId; + viewCtrl.title = mapId; + viewCtrl.divId = nil; + [viewCtrl.view setHidden:YES]; + + // Create an instance of the Map class everytime. + PluginMap *pluginMap = [[PluginMap alloc] init]; + [pluginMap pluginInitialize]; + pluginMap.mapCtrl = viewCtrl; + + // Hack: + // In order to load the plugin instance of the same class but different names, + // register the map plugin instance into the pluginObjects directly. + if ([pluginMap respondsToSelector:@selector(setViewController:)]) { + [pluginMap setViewController:cdvViewController]; + } + if ([pluginMap respondsToSelector:@selector(setCommandDelegate:)]) { + [pluginMap setCommandDelegate:cdvViewController.commandDelegate]; + } + [cdvViewController.pluginObjects setObject:pluginMap forKey:mapId]; + [cdvViewController.pluginsMap setValue:mapId forKey:mapId]; + [pluginMap pluginInitialize]; + + [self.viewPlugins setObject:pluginMap forKey:mapId]; + + CGRect rect = CGRectZero; + // Sets the map div id. + if ([command.arguments count] == 3) { + pluginMap.mapCtrl.divId = [command.arguments objectAtIndex:2]; + if (pluginMap.mapCtrl.divId != nil) { + NSDictionary *domInfo = [self.pluginLayer.pluginScrollView.HTMLNodes objectForKey:pluginMap.mapCtrl.divId]; + if (domInfo != nil) { + rect = CGRectFromString([domInfo objectForKey:@"size"]); + } + } + } + + + // Generate an instance of GMSMapView; + GMSCameraPosition *camera = nil; + int bearing = 0; + double angle = 0, zoom = 0; + NSDictionary *latLng = nil; + double latitude = 0; + double longitude = 0; + GMSCoordinateBounds *cameraBounds = nil; + NSDictionary *cameraOptions = [initOptions valueForKey:@"camera"]; + if (cameraOptions) { + + if ([cameraOptions valueForKey:@"bearing"] && [cameraOptions valueForKey:@"bearing"] != [NSNull null]) { + bearing = (int)[[cameraOptions valueForKey:@"bearing"] integerValue]; + } else { + bearing = 0; + } + + if ([cameraOptions valueForKey:@"tilt"] && [cameraOptions valueForKey:@"tilt"] != [NSNull null]) { + angle = [[cameraOptions valueForKey:@"tilt"] doubleValue]; + } else { + angle = 0; + } + + if ([cameraOptions valueForKey:@"zoom"] && [cameraOptions valueForKey:@"zoom"] != [NSNull null]) { + zoom = [[cameraOptions valueForKey:@"zoom"] doubleValue]; + } else { + zoom = 0; + } + if ([cameraOptions objectForKey:@"target"] && [cameraOptions valueForKey:@"target"] != [NSNull null]) { + NSString *targetClsName = [[cameraOptions objectForKey:@"target"] className]; + if ([targetClsName isEqualToString:@"__NSCFArray"] || [targetClsName isEqualToString:@"__NSArrayM"] ) { + //-------------------------------------------- + // cameraPosition.target = [ + // new plugin.google.maps.LatLng(), + // ... + // new plugin.google.maps.LatLng() + // ] + //--------------------------------------------- + int i = 0; + NSArray *latLngList = [cameraOptions objectForKey:@"target"]; + GMSMutablePath *path = [GMSMutablePath path]; + for (i = 0; i < [latLngList count]; i++) { + latLng = [latLngList objectAtIndex:i]; + latitude = [[latLng valueForKey:@"lat"] doubleValue]; + longitude = [[latLng valueForKey:@"lng"] doubleValue]; + [path addLatitude:latitude longitude:longitude]; + } + + cameraBounds = [[GMSCoordinateBounds alloc] initWithPath:path]; + //CLLocationCoordinate2D center = cameraBounds.center; + + latitude = cameraBounds.center.latitude; + longitude = cameraBounds.center.longitude; + } else { + //------------------------------------------------------------------ + // cameraPosition.target = new plugin.google.maps.LatLng(); + //------------------------------------------------------------------ + + latLng = [cameraOptions objectForKey:@"target"]; + latitude = [[latLng valueForKey:@"lat"] floatValue]; + longitude = [[latLng valueForKey:@"lng"] floatValue]; + + } + } + //[pluginMap.mapCtrl.view setHidden:YES]; + } + camera = [GMSCameraPosition cameraWithLatitude:latitude + longitude:longitude + zoom: zoom + bearing: bearing + viewingAngle: angle]; + + viewCtrl.map = [GMSMapView mapWithFrame:rect camera:camera]; + viewCtrl.view = viewCtrl.map; + + //mapType + NSString *typeStr = [initOptions valueForKey:@"mapType"]; + if (typeStr) { + + NSDictionary *mapTypes = [NSDictionary dictionaryWithObjectsAndKeys: + ^() {return kGMSTypeHybrid; }, @"MAP_TYPE_HYBRID", + ^() {return kGMSTypeSatellite; }, @"MAP_TYPE_SATELLITE", + ^() {return kGMSTypeTerrain; }, @"MAP_TYPE_TERRAIN", + ^() {return kGMSTypeNormal; }, @"MAP_TYPE_NORMAL", + ^() {return kGMSTypeNone; }, @"MAP_TYPE_NONE", + nil]; + + typedef GMSMapViewType (^CaseBlock)(); + GMSMapViewType mapType; + CaseBlock caseBlock = mapTypes[typeStr]; + if (caseBlock) { + // Change the map type + mapType = caseBlock(); + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + ((GMSMapView *)(viewCtrl.view)).mapType = mapType; + }]; + } + } + viewCtrl.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + + //indoor display + ((GMSMapView *)(viewCtrl.view)).delegate = viewCtrl; + ((GMSMapView *)(viewCtrl.view)).indoorDisplay.delegate = viewCtrl; + [self.pluginLayer addPluginOverlay:viewCtrl]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [pluginMap getMap:command]; + }); + + }); +} + +/** + * Intialize the panorama + */ +- (void)getPanorama:(CDVInvokedUrlCommand *)command { + if (self.pluginLayer != nil) { + self.pluginLayer.isSuspended = false; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + NSDictionary *meta = [command.arguments objectAtIndex:0]; + NSString *panoramaId = [meta objectForKey:@"__pgmId"]; + NSString *divId = [command.arguments objectAtIndex:2]; + + // Wrapper view + PluginStreetViewPanoramaController* panoramaCtrl = [[PluginStreetViewPanoramaController alloc] initWithOptions:nil]; + panoramaCtrl.webView = self.webView; + panoramaCtrl.isFullScreen = YES; + panoramaCtrl.overlayId = panoramaId; + panoramaCtrl.divId = divId; + panoramaCtrl.title = panoramaId; + //[mapCtrl.view setHidden:YES]; + + // Create an instance of the PluginStreetViewPanorama class everytime. + PluginStreetViewPanorama *pluginStreetView = [[PluginStreetViewPanorama alloc] init]; + [pluginStreetView pluginInitialize]; + + // Hack: + // In order to load the plugin instance of the same class but different names, + // register the street view plugin instance into the pluginObjects directly. + if ([pluginStreetView respondsToSelector:@selector(setViewController:)]) { + [pluginStreetView setViewController:cdvViewController]; + } + if ([pluginStreetView respondsToSelector:@selector(setCommandDelegate:)]) { + [pluginStreetView setCommandDelegate:cdvViewController.commandDelegate]; + } + [cdvViewController.pluginObjects setObject:pluginStreetView forKey:panoramaId]; + [cdvViewController.pluginsMap setValue:panoramaId forKey:panoramaId]; + [pluginStreetView pluginInitialize]; + + [self.viewPlugins setObject:pluginStreetView forKey:panoramaId]; + + CGRect rect = CGRectZero; + // Sets the panorama div id. + pluginStreetView.panoramaCtrl = panoramaCtrl; + pluginStreetView.panoramaCtrl.divId = divId; + if (pluginStreetView.panoramaCtrl.divId != nil) { + NSDictionary *domInfo = [self.pluginLayer.pluginScrollView.HTMLNodes objectForKey:pluginStreetView.panoramaCtrl.divId]; + if (domInfo != nil) { + rect = CGRectFromString([domInfo objectForKey:@"size"]); + } + } + + panoramaCtrl.panoramaView = [GMSPanoramaView panoramaWithFrame:rect nearCoordinate: CLLocationCoordinate2DMake(0, 0)]; + panoramaCtrl.view = panoramaCtrl.panoramaView; + panoramaCtrl.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + ((GMSPanoramaView *)(panoramaCtrl.view)).delegate = panoramaCtrl; + [self.pluginLayer addPluginOverlay:panoramaCtrl]; + + [pluginStreetView getPanorama:command]; + + }); +} + + +- (void)clearHtmlElements:(CDVInvokedUrlCommand *)command { + [self.executeQueue addOperationWithBlock:^{ + if (self.pluginLayer != nil) { + [self.pluginLayer clearHTMLElements]; + } + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)resume:(CDVInvokedUrlCommand *)command { + if (self.pluginLayer != nil) { + self.pluginLayer.isSuspended = NO; + } + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + +} +- (void)pause:(CDVInvokedUrlCommand *)command { + if (self.pluginLayer != nil) { + if (!self.pluginLayer.isSuspended) { + self.pluginLayer.isSuspended = YES; + // cancel the timer + [self.pluginLayer stopRedrawTimer]; + + //[self.pluginLayer resizeTask:nil]; + } + } + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} +- (void)updateMapPositionOnly:(CDVInvokedUrlCommand *)command { + [self.executeQueue addOperationWithBlock:^{ + if (self.pluginLayer != nil) { + + NSDictionary *elementsDic = [command.arguments objectAtIndex:0]; + NSString *domId; + CGRect rect = CGRectMake(0, 0, 0, 0); + NSMutableDictionary *domInfo, *size, *currentDomInfo; + @synchronized(self.pluginLayer.pluginScrollView.HTMLNodes) { + for (domId in elementsDic) { + + domInfo = [elementsDic objectForKey:domId]; + size = [domInfo objectForKey:@"size"]; + rect.origin.x = [[size objectForKey:@"left"] doubleValue]; + rect.origin.y = [[size objectForKey:@"top"] doubleValue]; + rect.size.width = [[size objectForKey:@"width"] doubleValue]; + rect.size.height = [[size objectForKey:@"height"] doubleValue]; + + currentDomInfo = [self.pluginLayer.pluginScrollView.HTMLNodes objectForKey:domId]; + if (currentDomInfo == nil) { + currentDomInfo = domInfo; + } + [currentDomInfo setValue:NSStringFromCGRect(rect) forKey:@"size"]; + [self.pluginLayer.pluginScrollView.HTMLNodes setObject:currentDomInfo forKey:domId]; + } + } + + self.pluginLayer.isSuspended = false; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [self.pluginLayer resizeTask:nil]; + }]; + } + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)putHtmlElements:(CDVInvokedUrlCommand *)command { + [self.executeQueue addOperationWithBlock:^{ + + NSDictionary *elements = [command.arguments objectAtIndex:0]; + + if (self.pluginLayer != nil) { + [self.pluginLayer putHTMLElements:elements]; + } + /* + if (self.pluginLayer.needUpdatePosition) { + self.pluginLayer.needUpdatePosition = NO; + NSArray *keys=[self.viewPlugins allKeys]; + NSString *mapId; + PluginMap *pluginMap; + + for (int i = 0; i < [keys count]; i++) { + mapId = [keys objectAtIndex:i]; + pluginMap = [self.viewPlugins objectForKey:mapId]; + [self.pluginLayer updateViewPosition:pluginMap.mapCtrl]; + } + } + */ + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + pluginResult = nil; + elements = nil; + }]; +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/GMSCoordinateBounds+Geometry.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/GMSCoordinateBounds+Geometry.h new file mode 100644 index 0000000..b93e288 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/GMSCoordinateBounds+Geometry.h @@ -0,0 +1,31 @@ +// +// GMSCoordinateBounds+Geometry.h +// +// Created by Marius Feldmann on 8/12/14. +// Copyright (c) 2014 Marius Feldmann. All rights reserved. +// + +#import + +@interface GMSCoordinateBounds (MFAdditions) + +/** The North-West corner of these bounds. */ +- (CLLocationCoordinate2D)southEast; + +/** The South-East corner of these bounds. */ +- (CLLocationCoordinate2D)northWest; + +/** The center coordinate of these bounds. */ +- (CLLocationCoordinate2D)center; + +/** Return the path of the rect. */ +- (GMSPath *)path; + +/** + * Returns an NSArray of GMSCoordinateBounds + * Divides the current rectangular bounding box + * into |numberOfRects| smaller boxes. + */ +- (NSArray *)divideIntoNumberOfBoxes:(NSInteger)numberOfBoxesl; + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/GMSCoordinateBounds+Geometry.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/GMSCoordinateBounds+Geometry.m new file mode 100644 index 0000000..fea616c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/GMSCoordinateBounds+Geometry.m @@ -0,0 +1,106 @@ +// +// GMSCoordinateBounds+Geometry.m +// +// Created by Marius Feldmann on 8/12/14. +// Copyright (c) 2014 Marius Feldmann. All rights reserved. +// + +#import "GMSCoordinateBounds+Geometry.h" + +#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI)) + +@implementation GMSCoordinateBounds (MFAdditions) + +- (CLLocationCoordinate2D)southEast { + CLLocationDegrees southEastLat = self.southWest.latitude; + CLLocationDegrees southEastLng = self.northEast.longitude; + CLLocationCoordinate2D southEast = CLLocationCoordinate2DMake(southEastLat, southEastLng); + + return southEast; +} + + +- (CLLocationCoordinate2D)northWest { + CLLocationDegrees northWestLat = self.northEast.latitude; + CLLocationDegrees northWestLng = self.southWest.longitude; + CLLocationCoordinate2D northWest = CLLocationCoordinate2DMake(northWestLat, northWestLng); + + return northWest; +} + + +- (CLLocationCoordinate2D)center { + double fraction = 0.5; + CLLocationCoordinate2D center = GMSGeometryInterpolate(self.northEast, self.southWest, fraction); + + return center; +} + + +- (GMSPath *)path { + GMSMutablePath *path = [[GMSMutablePath alloc] init]; + [path addCoordinate:self.northWest]; + [path addCoordinate:self.northEast]; + [path addCoordinate:self.southEast]; + [path addCoordinate:self.southWest]; + + return path; +} + + +- (NSArray *)divideIntoNumberOfBoxes:(NSInteger)numberOfBoxes { + NSMutableArray *rectArray = [[NSMutableArray alloc] init]; + + NSInteger columns = ceil(sqrt(numberOfBoxes)); + NSInteger fullRows = numberOfBoxes / columns; + NSInteger orphans = numberOfBoxes % columns; + + double width = GMSGeometryDistance(self.northWest, self.northEast); + double boxWidth = width / columns; + + double height = GMSGeometryDistance(self.northEast, self.southEast); + double boxHeight = height / (orphans == 0 ? fullRows : (fullRows+1)); + + for (int y=0; y 0) { + NSInteger orphanWidth = width / orphans; + + for (int x=0; x +#import +#import "PluginViewController.h" + +@protocol IPluginProtocol +//- (void)onHookedPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId; +- (void)pluginUnload; +- (void)setPluginViewController: (PluginViewController*)viewCtrl; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/IPluginView.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/IPluginView.h new file mode 100644 index 0000000..acf4aed --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/IPluginView.h @@ -0,0 +1,14 @@ +// +// IPluginView +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import + +@protocol IPluginView +- (void)attachToWebView:(CDVInvokedUrlCommand*)command; +- (void)detachFromWebView:(CDVInvokedUrlCommand*)command; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.h new file mode 100644 index 0000000..b93e288 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.h @@ -0,0 +1,31 @@ +// +// GMSCoordinateBounds+Geometry.h +// +// Created by Marius Feldmann on 8/12/14. +// Copyright (c) 2014 Marius Feldmann. All rights reserved. +// + +#import + +@interface GMSCoordinateBounds (MFAdditions) + +/** The North-West corner of these bounds. */ +- (CLLocationCoordinate2D)southEast; + +/** The South-East corner of these bounds. */ +- (CLLocationCoordinate2D)northWest; + +/** The center coordinate of these bounds. */ +- (CLLocationCoordinate2D)center; + +/** Return the path of the rect. */ +- (GMSPath *)path; + +/** + * Returns an NSArray of GMSCoordinateBounds + * Divides the current rectangular bounding box + * into |numberOfRects| smaller boxes. + */ +- (NSArray *)divideIntoNumberOfBoxes:(NSInteger)numberOfBoxesl; + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.m new file mode 100644 index 0000000..fea616c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.m @@ -0,0 +1,106 @@ +// +// GMSCoordinateBounds+Geometry.m +// +// Created by Marius Feldmann on 8/12/14. +// Copyright (c) 2014 Marius Feldmann. All rights reserved. +// + +#import "GMSCoordinateBounds+Geometry.h" + +#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI)) + +@implementation GMSCoordinateBounds (MFAdditions) + +- (CLLocationCoordinate2D)southEast { + CLLocationDegrees southEastLat = self.southWest.latitude; + CLLocationDegrees southEastLng = self.northEast.longitude; + CLLocationCoordinate2D southEast = CLLocationCoordinate2DMake(southEastLat, southEastLng); + + return southEast; +} + + +- (CLLocationCoordinate2D)northWest { + CLLocationDegrees northWestLat = self.northEast.latitude; + CLLocationDegrees northWestLng = self.southWest.longitude; + CLLocationCoordinate2D northWest = CLLocationCoordinate2DMake(northWestLat, northWestLng); + + return northWest; +} + + +- (CLLocationCoordinate2D)center { + double fraction = 0.5; + CLLocationCoordinate2D center = GMSGeometryInterpolate(self.northEast, self.southWest, fraction); + + return center; +} + + +- (GMSPath *)path { + GMSMutablePath *path = [[GMSMutablePath alloc] init]; + [path addCoordinate:self.northWest]; + [path addCoordinate:self.northEast]; + [path addCoordinate:self.southEast]; + [path addCoordinate:self.southWest]; + + return path; +} + + +- (NSArray *)divideIntoNumberOfBoxes:(NSInteger)numberOfBoxes { + NSMutableArray *rectArray = [[NSMutableArray alloc] init]; + + NSInteger columns = ceil(sqrt(numberOfBoxes)); + NSInteger fullRows = numberOfBoxes / columns; + NSInteger orphans = numberOfBoxes % columns; + + double width = GMSGeometryDistance(self.northWest, self.northEast); + double boxWidth = width / columns; + + double height = GMSGeometryDistance(self.northEast, self.southEast); + double boxHeight = height / (orphans == 0 ? fullRows : (fullRows+1)); + + for (int y=0; y 0) { + NSInteger orphanWidth = width / orphans; + + for (int x=0; x +#import "PluginMapViewController.h" +#import "MyPluginScrollView.h" + +@interface CGDOMRect : NSObject + @property CGFloat left; + @property CGFloat right; + @property CGFloat top; + @property CGFloat bottom; +@end + +@interface OverflowCSS : NSObject + @property BOOL cropX; + @property BOOL cropY; + @property CGRect rect; +@end + +@interface MyPluginLayer : UIView + +@property (nonatomic) UIView *webView; +@property (nonatomic) MyPluginScrollView *pluginScrollView; +@property (atomic) NSTimer *redrawTimer; +@property (nonatomic) BOOL isSuspended; +@property (nonatomic) NSOperationQueue *executeQueue; +@property (atomic, strong) NSObject *_lockObject; +@property (atomic, strong) NSMutableDictionary *CACHE_FIND_DOM; + +- (id)initWithWebView:(UIView *)webView; +- (void)resizeTask:(NSTimer *)timer; +- (void)clearHTMLElements; +- (void)putHTMLElements:(NSDictionary *)elementsDic; +- (void)addPluginOverlay:(PluginViewController *)pluginViewCtrl; +- (void)removePluginOverlay:(PluginViewController *)pluginViewCtrl; +- (void)updateViewPosition:(PluginViewController *)pluginViewCtrl; +- (void)startRedrawTimer; +- (void)stopRedrawTimer; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginLayer.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginLayer.m new file mode 100644 index 0000000..add7a8a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginLayer.m @@ -0,0 +1,695 @@ +// +// MyPluginLayer.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "MyPluginLayer.h" + +@implementation OverflowCSS : NSObject + BOOL cropX; + BOOL cropY; + CGRect rect; +@end + + +@implementation CGDOMRect : NSObject + CGFloat left; + CGFloat right; + CGFloat top; + CGFloat bottom; +@end + +@implementation MyPluginLayer + +- (id)initWithWebView:(UIView *)webView { + self.executeQueue = [NSOperationQueue new]; + self._lockObject = [[NSObject alloc] init]; + self.CACHE_FIND_DOM = [NSMutableDictionary dictionary]; + + self = [super initWithFrame:[webView frame]]; + self.webView = webView; + self.isSuspended = false; + self.opaque = NO; + [self.webView removeFromSuperview]; + // prevent webView from bouncing + if ([self.webView respondsToSelector:@selector(scrollView)]) { + ((UIScrollView*)[self.webView scrollView]).bounces = NO; + } else { + for (id subview in [self.webView subviews]) { + if ([[subview class] isSubclassOfClass:[UIScrollView class]]) { + ((UIScrollView*)subview).bounces = NO; + } + } + } + + self.pluginScrollView = [[MyPluginScrollView alloc] initWithFrame:[self.webView frame]]; + + self.pluginScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + self.pluginScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + UIView *uiview = self.webView; + uiview.scrollView.delegate = self; + [self.pluginScrollView setContentSize:self.webView.scrollView.frame.size ]; + + [self addSubview:self.pluginScrollView]; + + [self addSubview:self.webView]; + + +// dispatch_queue_t q_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); +// +// dispatch_async(q_background, ^{ +// [self startRedrawTimer]; +// }); + + return self; +} + +- (void)startRedrawTimer { + @synchronized(self._lockObject) { + if (!self.redrawTimer) { + + self.redrawTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 + target:self + selector:@selector(resizeTask:) + userInfo:nil + repeats:YES]; + } + } +} +- (void)stopRedrawTimer { + @synchronized(self._lockObject) { + if (self.redrawTimer) { + [self.redrawTimer invalidate]; + self.redrawTimer = nil; + } + } +} +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + CGPoint offset = self.webView.scrollView.contentOffset; + self.pluginScrollView.contentOffset = offset; +} + +-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { + CGPoint offset = self.webView.scrollView.contentOffset; + self.pluginScrollView.contentOffset = offset; +} +- (void)scrollViewDidScroll:(UIScrollView *)scrollView { + CGPoint offset = self.webView.scrollView.contentOffset; + self.pluginScrollView.contentOffset = offset; +} +- (void)clearHTMLElements { + @synchronized(self.pluginScrollView.HTMLNodes) { + NSMutableDictionary *domInfo; + NSString *domId; + NSArray *keys=[self.pluginScrollView.HTMLNodes allKeys]; + NSArray *keys2; + int i, j; + for (i = 0; i < [keys count]; i++) { + domId = [keys objectAtIndex:i]; + domInfo = [self.pluginScrollView.HTMLNodes objectForKey:domId]; + if (domInfo) { + keys2 = [domInfo allKeys]; + for (j = 0; j < [keys2 count]; j++) { + [domInfo removeObjectForKey:[keys2 objectAtIndex:j]]; + } + domInfo = nil; + } + [self.pluginScrollView.HTMLNodes removeObjectForKey:domId]; + } + } + +} + +- (void)putHTMLElements:(NSDictionary *)elementsDic { + + [self.executeQueue addOperationWithBlock:^{ + CGRect rect = CGRectMake(0, 0, 0, 0); + NSMutableDictionary *domInfo, *size; + NSString *domId; + + @synchronized(self._lockObject) { + + if (self.pluginScrollView.HTMLNodes != nil) { + NSArray *keys=[self.pluginScrollView.HTMLNodes allKeys]; + NSArray *keys2; + int i, j; + for (i = 0; i < [keys count]; i++) { + domId = [keys objectAtIndex:i]; + domInfo = [self.pluginScrollView.HTMLNodes objectForKey:domId]; + if (domInfo) { + keys2 = [domInfo allKeys]; + for (j = 0; j < [keys2 count]; j++) { + [domInfo removeObjectForKey:[keys2 objectAtIndex:j]]; + } + domInfo = nil; + } + [self.pluginScrollView.HTMLNodes removeObjectForKey:domId]; + } + } + self.pluginScrollView.HTMLNodes = nil; + + NSMutableDictionary *newBuffer = [[NSMutableDictionary alloc] init]; + for (domId in elementsDic) { + domInfo = [elementsDic objectForKey:domId]; + size = [domInfo objectForKey:@"size"]; + rect.origin.x = [[size objectForKey:@"left"] doubleValue]; + rect.origin.y = [[size objectForKey:@"top"] doubleValue]; + rect.size.width = [[size objectForKey:@"width"] doubleValue]; + rect.size.height = [[size objectForKey:@"height"] doubleValue]; + [domInfo setValue:NSStringFromCGRect(rect) forKey:@"size"]; + [newBuffer setObject:domInfo forKey:domId]; + + domInfo = nil; + size = nil; + } + + self.pluginScrollView.HTMLNodes = newBuffer; + } + }]; + +} +- (void)addPluginOverlay:(PluginViewController *)pluginViewCtrl { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + // Hold the mapCtrl instance with mapId. + [self.pluginScrollView.mapCtrls setObject:pluginViewCtrl forKey:pluginViewCtrl.overlayId]; + + //When showing this plugin multiple times, a UIView from other plugin might be overlayed on + //top of this plugin so we must switch the view with the view that is right before the webview + //and update the background color for the webview to prevent issues with non transparent webview + [self exchangeSubviewAtIndex:[self.subviews indexOfObject:self.webView]-1 withSubviewAtIndex:[self.subviews indexOfObject:self.pluginScrollView]]; + self.webView.backgroundColor = [UIColor clearColor]; + + // Add the mapView under the scroll view. + [pluginViewCtrl.view setTag:pluginViewCtrl.viewDepth]; + [self.pluginScrollView attachView:pluginViewCtrl.view depth:pluginViewCtrl.viewDepth]; + pluginViewCtrl.attached = YES; + + [self updateViewPosition:pluginViewCtrl]; + }]; +} + +- (void)removePluginOverlay:(PluginViewController *)pluginViewCtrl { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + pluginViewCtrl.attached = NO; + + // Remove the mapCtrl instance with mapId. + [self.pluginScrollView.mapCtrls removeObjectForKey:pluginViewCtrl.overlayId]; + + // Remove the mapView from the scroll view. + [pluginViewCtrl willMoveToParentViewController:nil]; + [pluginViewCtrl.view removeFromSuperview]; + [pluginViewCtrl removeFromParentViewController]; + [self.pluginScrollView detachView:pluginViewCtrl.view]; + + //[pluginViewCtrl.view setFrame:CGRectMake(0, -pluginViewCtrl.view.frame.size.height, pluginViewCtrl.view.frame.size.width, pluginViewCtrl.view.frame.size.height)]; + //[pluginViewCtrl.view setNeedsDisplay]; + }]; + +} + +- (void)resizeTask:(NSTimer *)timer { + dispatch_async(dispatch_get_main_queue(), ^{ + NSArray *keys=[self.pluginScrollView.mapCtrls allKeys]; + NSString *mapId; + PluginMapViewController *mapCtrl; + + + for (int i = 0; i < [keys count]; i++) { + mapId = [keys objectAtIndex:i]; + mapCtrl = [self.pluginScrollView.mapCtrls objectForKey:mapId]; + [self updateViewPosition:mapCtrl]; + } + }); +} + +- (void)updateViewPosition:(PluginViewController *)pluginViewCtrl { + CGFloat zoomScale = self.webView.scrollView.zoomScale; + [self.pluginScrollView setFrame:self.webView.frame]; + + CGPoint offset = self.webView.scrollView.contentOffset; + offset.x *= zoomScale; + offset.y *= zoomScale; + [self.pluginScrollView setContentOffset:offset]; + + if (!pluginViewCtrl.divId) { + return; + } + + NSDictionary *domInfo = nil; + @synchronized(self.pluginScrollView.HTMLNodes) { + domInfo = [self.pluginScrollView.HTMLNodes objectForKey:pluginViewCtrl.divId]; + if (domInfo == nil) { + return; + } + + } + NSString *rectStr = [domInfo objectForKey:@"size"]; + if (rectStr == nil || [rectStr isEqual: @"null"]) { + return; + } + + __block CGRect rect = CGRectFromString(rectStr); + if (rect.origin.x == 0 && + rect.origin.y == 0 && + rect.size.width == 0 && + rect.size.height == 0) { + return; + } + rect.origin.x *= zoomScale; + rect.origin.y *= zoomScale; + rect.size.width *= zoomScale; + rect.size.height *= zoomScale; + rect.origin.x += offset.x; + rect.origin.y += offset.y; + + float webviewWidth = self.webView.frame.size.width; + float webviewHeight = self.webView.frame.size.height; + + + // Is the map is displayed? + if (pluginViewCtrl.view.hidden == NO) { + if (pluginViewCtrl.isRenderedAtOnce == YES || + ([pluginViewCtrl.overlayId hasPrefix:@"panorama_"] || + ([pluginViewCtrl.overlayId hasPrefix:@"map_"] && + ((GMSMapView *)pluginViewCtrl.view).mapType != kGMSTypeSatellite && + ((GMSMapView *)pluginViewCtrl.view).mapType != kGMSTypeHybrid) + )) { + + if (rect.origin.y + rect.size.height >= offset.y && + rect.origin.x + rect.size.width >= offset.x && + rect.origin.y < offset.y + webviewHeight && + rect.origin.x < offset.x + webviewWidth) { + + // Attach the map view to the parent. + [pluginViewCtrl.view setTag:pluginViewCtrl.viewDepth]; + [self.pluginScrollView attachView:pluginViewCtrl.view depth:pluginViewCtrl.viewDepth]; + + } else { + + // Detach from the parent view + [pluginViewCtrl.view removeFromSuperview]; + } + } + } + + + if (pluginViewCtrl.isRenderedAtOnce == YES && + rect.origin.x == pluginViewCtrl.view.frame.origin.x && + rect.origin.y == pluginViewCtrl.view.frame.origin.y && + rect.size.width == pluginViewCtrl.view.frame.size.width && + rect.size.height == pluginViewCtrl.view.frame.size.height) { + return; + } + + if (pluginViewCtrl.attached) { +// if (pluginViewCtrl.isRenderedAtOnce) { +// +// __block int zPosition = pluginViewCtrl.view.layer.zPosition; +// [UIView animateWithDuration:0.075f animations:^{ +// [pluginViewCtrl.view setFrame:rect]; +// pluginViewCtrl.view.layer.zPosition = zPosition; +// //rect.origin.x = 0; +// //rect.origin.y = 0; +// }]; +// } else { + [pluginViewCtrl.view setFrame:rect]; + //rect.origin.x = 0; + //rect.origin.y = 0; + pluginViewCtrl.isRenderedAtOnce = YES; +// } + } else { + [pluginViewCtrl.view setFrame:CGRectMake(0, -pluginViewCtrl.view.frame.size.height, pluginViewCtrl.view.frame.size.width, pluginViewCtrl.view.frame.size.height)]; + } + +} +- (void)execJS: (NSString *)jsString { + if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) { + [self.webView performSelector:@selector(stringByEvaluatingJavaScriptFromString:) withObject:jsString]; + } else if ([self.webView respondsToSelector:@selector(evaluateJavaScript:completionHandler:)]) { + [self.webView performSelector:@selector(evaluateJavaScript:completionHandler:) withObject:jsString withObject:nil]; + } +} +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + CGPoint browserClickPoint = CGPointMake(point.x - self.webView.frame.origin.x, point.y - self.webView.frame.origin.y); + //NSLog(@"-->zoomScale = %f", self.webView.scrollView.zoomScale); + + [self.CACHE_FIND_DOM removeAllObjects]; + + // Check other views of other plugins before this plugin + // e.g. PhoneGap-Plugin-ListPicker, etc + UIView *subview; + NSArray *subviews = [self.webView.superview subviews]; + //CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + //CGPoint subviewPoint = CGPointMake(browserClickPoint.x, browserClickPoint.y - statusBarFrame.size.height); + + for (int i = ((int)[subviews count] - 1); i >= 0; i--) { + subview = [subviews objectAtIndex: i]; + //NSLog(@"--->subview[%d] = %@", i, subview); + // we only want to check against other views + if (subview == self.pluginScrollView) { + continue; + } + + if (subview.isHidden || !subview.isUserInteractionEnabled) { + continue; + } + + CGPoint subviewPoint = CGPointMake(point.x, point.y - subview.frame.origin.y); + UIView *hit = [subview hitTest:subviewPoint withEvent:event]; + + if (hit) { + if (subview == self.webView) { + break; + } + return hit; + } + } + if (self.pluginScrollView.mapCtrls == nil || self.pluginScrollView.mapCtrls.count == 0) { + // Assumes all touches for the browser + //NSLog(@"--->browser!"); + return [self.webView hitTest:browserClickPoint withEvent:event]; + } + + float offsetX = self.webView.scrollView.contentOffset.x; + float offsetY = self.webView.scrollView.contentOffset.y; + + float webviewWidth = self.webView.frame.size.width; + float webviewHeight = self.webView.frame.size.height; + + + CGRect rect; + NSEnumerator *mapIDs = [self.pluginScrollView.mapCtrls keyEnumerator]; + PluginMapViewController *mapCtrl; + id mapId; + NSString *clickedDomId; + + CGFloat zoomScale = [[UIScreen mainScreen] scale]; + offsetY *= zoomScale; + offsetX *= zoomScale; + webviewWidth *= zoomScale; + webviewHeight *= zoomScale; + + NSDictionary *domInfo; + + @synchronized(self.pluginScrollView.HTMLNodes) { + //NSLog(@"--->browserClickPoint = %f, %f", browserClickPoint.x, browserClickPoint.y); + clickedDomId = [self findClickedDom:@"root" withPoint:browserClickPoint isMapChild:NO overflow:nil]; + //NSLog(@"--->clickedDomId = %@", clickedDomId); + + while(mapId = [mapIDs nextObject]) { + mapCtrl = [self.pluginScrollView.mapCtrls objectForKey:mapId]; + if (!mapCtrl.divId) { + continue; + } + domInfo =[self.pluginScrollView.HTMLNodes objectForKey:mapCtrl.divId]; + + rect = CGRectFromString([domInfo objectForKey:@"size"]); + + // Is the map clickable? + if (mapCtrl.clickable == NO) { + //NSLog(@"--> map (%@) is not clickable.", mapCtrl.overlayId); + continue; + } + + // Is the map displayed? + if (rect.origin.y + rect.size.height < 0 || + rect.origin.x + rect.size.width < 0 || + rect.origin.y > offsetY + webviewHeight || + rect.origin.x > offsetX + webviewWidth || + mapCtrl.view.hidden == YES) { + //NSLog(@"--> map (%@) is not displayed.", mapCtrl.overlayId); + continue; + } + + // Is the clicked point is in the map rectangle? + if (CGRectContainsPoint(rect, browserClickPoint)) { + //NSLog(@"--->in map"); + + //NSLog(@"--->clickedDomId = %@, mapCtrl.divId = %@", clickedDomId, mapCtrl.divId); + if ([mapCtrl.divId isEqualToString:clickedDomId]) { + // If user click on the map, return the mapCtrl.view. + CGPoint mapPoint = CGPointMake(point.x - mapCtrl.view.frame.origin.x - self.webView.frame.origin.x, point.y - mapCtrl.view.frame.origin.y - self.webView.frame.origin.y); + + UIView *hitView =[mapCtrl.view hitTest:mapPoint withEvent:event]; + //[mapCtrl mapView:mapCtrl.map didTapAtPoint:point2]; + + return hitView; + } + } + + } + } + + //NSLog(@"--->in browser!"); + return [self.webView hitTest:browserClickPoint withEvent:event]; +} + +- (NSString *)findClickedDom:(NSString *)domId withPoint:(CGPoint)clickPoint isMapChild:(BOOL)isMapChild overflow:(OverflowCSS *)overflow { + + if ([self.CACHE_FIND_DOM objectForKey:domId]) { + NSString *cacheResult = [self.CACHE_FIND_DOM objectForKey:domId]; + if ([@"(null)" isEqualToString:cacheResult]) { + cacheResult = nil; + } + return cacheResult; + } + + OverflowCSS *overflow1 = nil; + if (overflow) { + overflow1 = [[OverflowCSS alloc] init]; + overflow1.cropX = overflow.cropX; + overflow1.cropY = overflow.cropY; + overflow1.rect = CGRectMake(overflow.rect.origin.x, overflow.rect.origin.y, overflow.rect.size.width, overflow.rect.size.height); + } + + NSDictionary *domInfo = [self.pluginScrollView.HTMLNodes objectForKey:domId]; + NSDictionary *domInfoChild; + NSString *maxDomId = nil; + CGRect rect, tmpRect; + CGDOMRect *rect2, *rect3; + NSDictionary *zIndexProp; + unsigned long containMapCnt = 0; + + domInfo = [self.pluginScrollView.HTMLNodes objectForKey:domId]; + if ([domInfo objectForKey:@"containMapIDs"]) { + NSDictionary *containMapIDs = [domInfo objectForKey:@"containMapIDs"]; + containMapCnt = [[containMapIDs allKeys] count]; + } + isMapChild = isMapChild || [[domInfo objectForKey:@"isMap"] boolValue] || false; + //NSLog(@"---- domId = %@, containMapCnt = %ld, isMapChild = %@", domId, containMapCnt, isMapChild ? @"YES":@"NO"); + + NSString *pointerEvents = [domInfo objectForKey:@"pointerEvents"]; + NSString *overflowX = [domInfo objectForKey:@"overflowX"]; + NSString *overflowY = [domInfo objectForKey:@"overflowY"]; + if ([@"hidden" isEqualToString:overflowX] || [@"scroll" isEqualToString:overflowX] || + [@"hidden" isEqualToString:overflowY] || [@"scroll" isEqualToString:overflowY]) { + overflow1 = [[OverflowCSS alloc] init]; + overflow1.cropX = [@"hidden" isEqualToString:overflowX] || [@"scroll" isEqualToString:overflowX]; + overflow1.cropY = [@"hidden" isEqualToString:overflowY] || [@"scroll" isEqualToString:overflowY]; + overflow1.rect = CGRectFromString([domInfo objectForKey:@"size"]); + } else if ([@"visible" isEqualToString:overflowX] || [@"visible" isEqualToString:overflowX]) { + if (overflow1 != nil) { + overflow1.cropX = ![@"visible" isEqualToString:overflowX]; + overflow1.cropY = ![@"visible" isEqualToString:overflowY]; + } + } + + zIndexProp = [domInfo objectForKey:@"zIndex"]; + NSArray *children = [domInfo objectForKey:@"children"]; + int maxZIndex = -1215752192; // -1 * pow(2, 32) + 1; + if ((containMapCnt > 0 || isMapChild || [@"none" isEqualToString:pointerEvents] || + [[zIndexProp objectForKey:@"isInherit"] boolValue]) && children != nil && children.count > 0) { + + int zIndexValue; + NSString *childId, *grandChildId; + NSArray *grandChildren; + + for (int i = (int)children.count - 1; i >= 0; i--) { + childId = [children objectAtIndex:i]; + domInfo = [self.pluginScrollView.HTMLNodes objectForKey:childId]; + if (!domInfo) { + continue; + } + + zIndexProp = [domInfo objectForKey:@"zIndex"]; + zIndexValue = [[zIndexProp objectForKey:@"z"] intValue]; + if (maxZIndex < zIndexValue || [[zIndexProp objectForKey:@"isInherit"] boolValue]) { + + grandChildren = [domInfo objectForKey:@"children"]; + if (grandChildren == nil || grandChildren.count == 0) { + tmpRect = CGRectFromString([domInfo objectForKey:@"size"]); + rect2 = [[CGDOMRect alloc] init]; + rect2.left = tmpRect.origin.x; + rect2.top = tmpRect.origin.y; + rect2.right = rect2.left + tmpRect.size.width; + rect2.bottom = rect2.top + tmpRect.size.height; + + if (overflow1 && ![@"root" isEqualToString:domId]) { + + overflow1.rect = CGRectFromString([domInfo objectForKey:@"size"]); + rect3 = [[CGDOMRect alloc] init]; + rect3.left = overflow1.rect.origin.x; + rect3.top = overflow1.rect.origin.y; + rect3.right = rect3.left + overflow1.rect.size.width; + rect3.bottom = rect3.top + overflow1.rect.size.height; + + + if (overflow1.cropX) { + rect2.left = MAX(rect2.left, rect3.left); + rect2.right = MIN(rect2.right, rect3.right); + } + if (overflow1.cropY) { + rect2.top = MAX(rect2.top, rect3.top); + rect2.bottom = MIN(rect2.bottom, rect3.bottom); + } + + } + + if (clickPoint.x < rect2.left || + clickPoint.y < rect2.top || + clickPoint.x > rect2.right || + clickPoint.y > rect2.bottom) { + continue; + } + + if ([@"none" isEqualToString:[domInfo objectForKey:@"pointerEvents"]]) { + continue; + } + + if (maxZIndex < zIndexValue) { + maxZIndex = zIndexValue; + maxDomId = childId; + } + + } else { + if (zIndexValue < maxZIndex) { + continue; + } + grandChildId = [self findClickedDom:childId withPoint:clickPoint isMapChild: isMapChild overflow:overflow1]; + //NSLog(@"-----[570] childId = %@ grandChildId = %@", childId, grandChildId); + if (grandChildId == nil) { + grandChildId = childId; + } else { + domInfoChild = [self.pluginScrollView.HTMLNodes objectForKey:grandChildId]; + zIndexProp = [domInfoChild objectForKey:@"zIndex"]; + zIndexValue = [[zIndexProp objectForKey:@"z"] intValue]; + } + domInfoChild = [self.pluginScrollView.HTMLNodes objectForKey:grandChildId]; + rect = CGRectFromString([domInfoChild objectForKey:@"size"]); + + + rect2 = [[CGDOMRect alloc] init]; + rect2.left = rect.origin.x; + rect2.top = rect.origin.y; + rect2.right = rect2.left + rect.size.width; + rect2.bottom = rect2.top + rect.size.height; + + /* + if (overflow1 && ![@"root" isEqualToString:domId]) { + + NSLog(@"-----[519] grandChildId = %@, size = %@", grandChildId, [domInfo objectForKey:@"size"]); + overflow1.rect = CGRectFromString([domInfo objectForKey:@"size"]); + rect3 = [[CGDOMRect alloc] init]; + rect3.left = overflow1.rect.origin.x; + rect3.top = overflow1.rect.origin.y; + rect3.right = rect3.left + overflow1.rect.size.width; + rect3.bottom = rect3.top + overflow1.rect.size.height; + + + if (overflow1.cropX) { + rect2.left = MAX(rect2.left, rect3.left); + rect2.right = MIN(rect2.right, rect3.right); + } + if (overflow1.cropY) { + rect2.top = MAX(rect2.top, rect3.top); + rect2.bottom = MIN(rect2.bottom, rect3.bottom); + } + + } + */ + if (clickPoint.x < rect2.left || + clickPoint.y < rect2.top || + clickPoint.x > rect2.right || + clickPoint.y > rect2.bottom) { + continue; + } + + + if ([@"none" isEqualToString:[domInfoChild objectForKey:@"pointerEvents"]]) { + continue; + } + if (maxZIndex < zIndexValue) { + maxZIndex = zIndexValue; + maxDomId = grandChildId; + } + } + + } + } + } + + if (maxDomId == nil && ![@"root" isEqualToString:domId]) { + if ([@"none" isEqualToString:pointerEvents]) { + [self.CACHE_FIND_DOM setValue:@"(null)" forKey:domId]; + return nil; + } + + domInfo = [self.pluginScrollView.HTMLNodes objectForKey:domId]; + rect = CGRectFromString([domInfo objectForKey:@"size"]); + + rect2 = [[CGDOMRect alloc] init]; + rect2.left = rect.origin.x; + rect2.top = rect.origin.y; + rect2.right = rect2.left + rect.size.width; + rect2.bottom = rect2.top + rect.size.height; + + +// if (overflow1) { +// +// overflow1.rect = CGRectFromString([domInfo objectForKey:@"size"]); +// rect3 = [[CGDOMRect alloc] init]; +// rect3.left = overflow1.rect.origin.x; +// rect3.top = overflow1.rect.origin.y; +// rect3.right = rect3.left + overflow1.rect.size.width; +// rect3.bottom = rect3.top + overflow1.rect.size.height; +// +// +// if (overflow1.cropX) { +// rect2.left = MAX(rect2.left, rect3.left); +// rect2.right = MIN(rect2.right, rect3.right); +// } +// if (overflow1.cropY) { +// rect2.top = MAX(rect2.top, rect3.top); +// rect2.bottom = MIN(rect2.bottom, rect3.bottom); +// } +// +// } + + if (clickPoint.x < rect2.left || + clickPoint.y < rect2.top || + clickPoint.x > rect2.right || + clickPoint.y > rect2.bottom) { + [self.CACHE_FIND_DOM setValue:@"(null)" forKey:domId]; + return nil; + } + + maxDomId = domId; + } + + if (!maxDomId) { + [self.CACHE_FIND_DOM setValue:@"(null)" forKey:domId]; + } else { + [self.CACHE_FIND_DOM setValue:maxDomId forKey:domId]; + } + + return maxDomId; +} + + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginScrollView.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginScrollView.h new file mode 100644 index 0000000..012464c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginScrollView.h @@ -0,0 +1,17 @@ +// +// MyPluginScrollView.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import + +@interface MyPluginScrollView : UIScrollView + +@property (nonatomic) NSMutableDictionary *mapCtrls; +@property (nonatomic) NSMutableDictionary *HTMLNodes; +- (void)attachView:(UIView *)view depth:(NSInteger)depth; +- (void)detachView:(UIView *)view; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginScrollView.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginScrollView.m new file mode 100644 index 0000000..ad9d35f --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/MyPluginScrollView.m @@ -0,0 +1,57 @@ +// +// MyPluginScrollView.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "MyPluginScrollView.h" + +@implementation MyPluginScrollView + + +- (id)initWithFrame:(CGRect)aRect +{ + self = [super initWithFrame:aRect]; + // Avoid the white bar that appears at the top of the map with iPhone iOS 11 + // See problem description here: https://github.com/mapsplugin/cordova-plugin-googlemaps/issues/1909 + // + // https://github.com/vidinoti/cordova-plugin-googlemaps/commit/0894072be260223f4ee833e422adf011af9740dd + if (@available(iOS 11, *)) { + self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } + self.HTMLNodes = [[NSMutableDictionary alloc] init]; + self.mapCtrls = [[NSMutableDictionary alloc] init]; + return self; +} + +- (void)attachView:(UIView *)view depth:(NSInteger)depth { + NSArray *subviews = [self subviews]; + UIView *subview; + NSInteger tag; + int viewCnt = (int)[subviews count]; + int index = viewCnt; + for (int i = 0; i < viewCnt; i++) { + subview = [subviews objectAtIndex: i]; + tag = subview.tag; + if (tag == 0) { + continue; + } + if (tag > depth) { + index = i; + break; + } + } + + [self insertSubview:view atIndex:index]; +} +- (void)detachView:(UIView *)view { + [view removeFromSuperview]; + +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + return [super hitTest:point withEvent:event]; +} +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginCircle.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginCircle.h new file mode 100644 index 0000000..6198c00 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginCircle.h @@ -0,0 +1,25 @@ +// +// PluginCircle.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" + +@interface PluginCircle : CDVPlugin +@property (nonatomic, strong) PluginMapViewController* mapCtrl; +@property (nonatomic) BOOL initialized; + +- (void)create:(CDVInvokedUrlCommand*)command; +- (void)setCenter:(CDVInvokedUrlCommand*)command; +- (void)setFillColor:(CDVInvokedUrlCommand*)command; +- (void)setStrokeColor:(CDVInvokedUrlCommand*)command; +- (void)setStrokeWidth:(CDVInvokedUrlCommand*)command; +- (void)setRadius:(CDVInvokedUrlCommand*)command; +- (void)setZIndex:(CDVInvokedUrlCommand*)command; +- (void)setVisible:(CDVInvokedUrlCommand*)command; +- (void)remove:(CDVInvokedUrlCommand*)command; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginCircle.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginCircle.m new file mode 100644 index 0000000..9e41651 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginCircle.m @@ -0,0 +1,345 @@ +// +// PluginCircle.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginCircle.h" + +@implementation PluginCircle + +- (void)pluginInitialize +{ + if (self.initialized) { + return; + } + self.initialized = YES; + [super pluginInitialize]; +} + +- (void)pluginUnload +{ + + // Plugin destroy + NSArray *keys = [self.mapCtrl.objects allKeys]; + NSString *key; + for (int i = 0; i < [keys count]; i++) { + key = [keys objectAtIndex:i]; + if ([key hasPrefix:@"circle_property"]) { + key = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + GMSCircle *circle = (GMSCircle *)[self.mapCtrl.objects objectForKey:key]; + circle.map = nil; + circle = nil; + } + [self.mapCtrl.objects removeObjectForKey:key]; + } + + key = nil; + keys = nil; + + NSString *pluginId = [NSString stringWithFormat:@"%@-circle", self.mapCtrl.overlayId]; + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + [cdvViewController.pluginObjects removeObjectForKey:pluginId]; + [cdvViewController.pluginsMap setValue:nil forKey:pluginId]; + pluginId = nil; +} +-(void)setPluginViewController:(PluginViewController *)viewCtrl +{ + self.mapCtrl = (PluginMapViewController *)viewCtrl; +} +-(void)create:(CDVInvokedUrlCommand *)command +{ + + NSDictionary *json = [command.arguments objectAtIndex:1]; + NSString *idBase = [command.arguments objectAtIndex:2]; + + NSDictionary *latLng = [json objectForKey:@"center"]; + double latitude = [[latLng valueForKey:@"lat"] doubleValue]; + double longitude = [[latLng valueForKey:@"lng"] doubleValue]; + CLLocationCoordinate2D position = CLLocationCoordinate2DMake(latitude, longitude); + + float radius = [[json valueForKey:@"radius"] floatValue]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + GMSCircle *circle = [GMSCircle circleWithPosition:position radius:radius]; + if ([json valueForKey:@"fillColor"] && [json valueForKey:@"fillColor"] != [NSNull null]) { + circle.fillColor = [[json valueForKey:@"fillColor"] parsePluginColor]; + } + if ([json valueForKey:@"strokeColor"] && [json valueForKey:@"strokeColor"] != [NSNull null]) { + circle.strokeColor = [[json valueForKey:@"strokeColor"] parsePluginColor]; + } + if ([json valueForKey:@"strokeWidth"] && [json valueForKey:@"strokeWidth"] != [NSNull null]) { + circle.strokeWidth = [[json valueForKey:@"strokeWidth"] floatValue]; + } + if ([json valueForKey:@"zIndex"] && [json valueForKey:@"zIndex"] != [NSNull null]) { + circle.zIndex = [[json valueForKey:@"zIndex"] floatValue]; + } + + BOOL isVisible = YES; + + // Visible property + NSString *visibleValue = [NSString stringWithFormat:@"%@", json[@"visible"]]; + if ([@"0" isEqualToString:visibleValue]) { + // false + isVisible = NO; + circle.map = nil; + } else { + // true or default + circle.map = ((GMSMapView *)(self.mapCtrl.view)); + } + BOOL isClickable = NO; + if ([json valueForKey:@"clickable"] != [NSNull null] && [[json valueForKey:@"clickable"] boolValue]) { + isClickable = YES; + } + + + // Since this plugin uses own touch-detection, + // set NO to the tappable property. + circle.tappable = NO; + + // Store the circle instance into self.objects + NSString *circleId = [NSString stringWithFormat:@"circle_%@", idBase]; + circle.title = circleId; + [self.mapCtrl.objects setObject:circle forKey: circleId]; + + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + //--------------------------- + // Keep the properties + //--------------------------- + NSString *propertyId = [NSString stringWithFormat:@"circle_property_%@", idBase]; + + // points + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + GMSMutablePath *mutablePath = [PluginUtil getMutablePathFromCircle:circle.position radius:circle.radius]; + //[properties setObject:mutablePath forKey:@"mutablePath"]; + // bounds (pre-calculate for click detection) + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + // isVisible + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"isVisible"]; + // isClickable + [properties setObject:[NSNumber numberWithBool:isClickable] forKey:@"isClickable"]; + // zIndex + [properties setObject:[NSNumber numberWithFloat:circle.zIndex] forKey:@"zIndex"];; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + //--------------------------- + // Result for JS + //--------------------------- + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + [result setObject:circleId forKey:@"__pgmId"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }); +} +/** + * Set center + * @params key + */ +-(void)setCenter:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *circleId = [command.arguments objectAtIndex:0]; + GMSCircle *circle = [self.mapCtrl.objects objectForKey:circleId]; + + double latitude = [[command.arguments objectAtIndex:1] doubleValue]; + double longitude = [[command.arguments objectAtIndex:2] doubleValue]; + CLLocationCoordinate2D center = CLLocationCoordinate2DMake(latitude, longitude); + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [circle setPosition:center]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set fill color + * @params key + */ +-(void)setFillColor:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *circleId = [command.arguments objectAtIndex:0]; + GMSCircle *circle = [self.mapCtrl.objects objectForKey:circleId]; + + NSArray *rgbColor = [command.arguments objectAtIndex:1]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [circle setFillColor:[rgbColor parsePluginColor]]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + + +/** + * Set stroke color + * @params key + */ +-(void)setStrokeColor:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *circleId = [command.arguments objectAtIndex:0]; + GMSCircle *circle = [self.mapCtrl.objects objectForKey:circleId]; + + NSArray *rgbColor = [command.arguments objectAtIndex:1]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [circle setStrokeColor:[rgbColor parsePluginColor]]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set stroke width + * @params key + */ +-(void)setStrokeWidth:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *circleId = [command.arguments objectAtIndex:0]; + GMSCircle *circle = [self.mapCtrl.objects objectForKey:circleId]; + + float width = [[command.arguments objectAtIndex:1] floatValue]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [circle setStrokeWidth:width]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set radius + * @params key + */ +-(void)setRadius:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *circleId = [command.arguments objectAtIndex:0]; + GMSCircle *circle = [self.mapCtrl.objects objectForKey:circleId]; + float radius = [[command.arguments objectAtIndex:1] floatValue]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [circle setRadius:radius]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +/** + * Set z-index + * @params key + */ +-(void)setZIndex:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *circleId = [command.arguments objectAtIndex:0]; + GMSCircle *circle = [self.mapCtrl.objects objectForKey:circleId]; + NSInteger zIndex = [[command.arguments objectAtIndex:1] integerValue]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [circle setZIndex:(int)zIndex]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set visibility + * @params key + */ +-(void)setVisible:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *key = [command.arguments objectAtIndex:0]; + GMSCircle *circle = (GMSCircle *)[self.mapCtrl.objects objectForKey:key]; + Boolean isVisible = [[command.arguments objectAtIndex:1] boolValue]; + + // Update the property + NSString *propertyId = [key stringByReplacingOccurrencesOfString:@"circle_" withString:@"circle_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"isVisible"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + if (isVisible) { + circle.map = ((GMSMapView *)(self.mapCtrl.view)); + } else { + circle.map = nil; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; +} + + +/** + * Set clickable + * @params key + */ +-(void)setClickable:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *key = [command.arguments objectAtIndex:0]; + //GMSCircle *circle = (GMSCircle *)[self.mapCtrl.objects objectForKey:key]; + Boolean isClickable = [[command.arguments objectAtIndex:1] boolValue]; + + // Update the property + NSString *propertyId = [key stringByReplacingOccurrencesOfString:@"circle_" withString:@"circle_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:isClickable] forKey:@"isClickable"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +/** + * Remove the circle + * @params key + */ +-(void)remove:(CDVInvokedUrlCommand *)command +{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *circleId = [command.arguments objectAtIndex:0]; + GMSCircle *circle = [self.mapCtrl.objects objectForKey:circleId]; + + NSString *propertyId = [circleId stringByReplacingOccurrencesOfString:@"circle_" withString:@"circle_property_"]; + [self.mapCtrl.objects removeObjectForKey:propertyId]; + + circle.map = nil; + [self.mapCtrl.objects removeObjectForKey:circleId]; + + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginEnvironment.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginEnvironment.h new file mode 100644 index 0000000..eaab12c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginEnvironment.h @@ -0,0 +1,18 @@ +// +// PluginEnvironment.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import +#import "CordovaGoogleMaps.h" + +@interface PluginEnvironment : CDVPlugin + +- (void)isAvailable:(CDVInvokedUrlCommand*)command; +- (void)setBackGroundColor:(CDVInvokedUrlCommand*)command; +- (void)getLicenseInfo:(CDVInvokedUrlCommand*)command; +- (void)setEnv:(CDVInvokedUrlCommand*)command; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginEnvironment.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginEnvironment.m new file mode 100644 index 0000000..61fbbc1 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginEnvironment.m @@ -0,0 +1,121 @@ +// +// PluginEnvironment.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginEnvironment.h" + +@implementation PluginEnvironment + +dispatch_queue_t queue; + +- (void)pluginInitialize +{ + queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); +} + +- (void)pluginUnload +{ + queue = nil; +} +- (void)isAvailable:(CDVInvokedUrlCommand *)command { + // Return true always in iOS. + dispatch_async(queue, ^{ + //------------------------------- + // Check the Google Maps API key + //------------------------------- + NSString *errorMsg = nil; + + NSString *APIKey = [((CDVViewController *)self.viewController).settings objectForKey:@"google_maps_ios_api_key"]; + + if (APIKey == nil) { + NSString *errorTitle = [PluginUtil PGM_LOCALIZATION:@"APIKEY_IS_UNDEFINED_TITLE"]; + NSString *errorMsg = [PluginUtil PGM_LOCALIZATION:@"APIKEY_IS_UNDEFINED_MESSAGE"]; + + UIAlertController* alert = [UIAlertController alertControllerWithTitle:errorTitle + message:errorMsg + preferredStyle:UIAlertControllerStyleAlert]; + + NSString *closeBtnLabel = [PluginUtil PGM_LOCALIZATION:@"CLOSE_BUTTON"]; + UIAlertAction* ok = [UIAlertAction actionWithTitle:closeBtnLabel + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + [self.viewController presentViewController:alert + animated:YES + completion:nil]; + return; + } + + /*--------------------------------------------------------------------------------------- + * If CFBundleExecutable is not English, the Google Maps SDK for iOS will crash. + * So must be English. + * + * If you want to use non-english name for your app, you need to change your config.xml like this. + * + * + * + * (english app name) + *---------------------------------------------------------------------------------------*/ + + NSDictionary *info = [[NSBundle mainBundle] infoDictionary]; + NSString *CFBundleExecutable = [info objectForKey:@"CFBundleExecutable"]; + + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[a-zA-Z0-9$@$!%*?&#^\\-_.\\s+]+$" options:NSRegularExpressionCaseInsensitive error:nil]; + if ([regex numberOfMatchesInString:CFBundleExecutable options:0 range:NSMakeRange(0, CFBundleExecutable.length)] == 0) { + errorMsg = [PluginUtil PGM_LOCALIZATION:@"APP_NAME_ERROR_MESSAGE"]; + } + + CDVPluginResult* pluginResult; + if (errorMsg) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMsg]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }); + +} + +- (void)setBackGroundColor:(CDVInvokedUrlCommand *)command { + + dispatch_async(queue, ^{ + // Load the GoogleMap.m + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + + NSArray *rgbColor = [command.arguments objectAtIndex:0]; + dispatch_async(dispatch_get_main_queue(), ^{ + googlemaps.pluginLayer.backgroundColor = [rgbColor parsePluginColor]; + }); + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }); +} + +- (void)getLicenseInfo:(CDVInvokedUrlCommand *)command { + + dispatch_async(queue, ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + NSString *txt = [GMSServices openSourceLicenseInfo]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:txt]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }); + }); +} + +- (void)setEnv:(CDVInvokedUrlCommand *)command { + // stub + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGeocoder.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGeocoder.h new file mode 100644 index 0000000..b1514b2 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGeocoder.h @@ -0,0 +1,19 @@ +// +// PluginGeocoder.h +// cordova-googlemaps-plugin v2 +// +// Created by Katsumata Masashi. +// +// + +#import "CordovaGoogleMaps.h" +#import "PluginUtil.h" +#import + +@interface PluginGeocoder : CDVPlugin +@property (nonatomic, strong) NSDictionary *codeForCountryDictionary; +@property (nonatomic, strong) NSOperationQueue *executeQueue; +- (void)geocode:(CDVInvokedUrlCommand*)command; +- (NSArray *)geocoder_callback:(NSArray *)placemarks error:(NSError *)error; + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGeocoder.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGeocoder.m new file mode 100644 index 0000000..2ccfcaa --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGeocoder.m @@ -0,0 +1,296 @@ +// +// PluginGeocoder.m +// cordova-googlemaps-plugin v2 +// +// Created by Katsumata Masashi. +// +// + +#import "PluginGeocoder.h" + +@implementation PluginGeocoder + +- (void)pluginUnload +{ + if (self.executeQueue != nil){ + self.executeQueue.suspended = YES; + [self.executeQueue cancelAllOperations]; + self.executeQueue.suspended = NO; + self.executeQueue = nil; + } +} +- (void)pluginInitialize +{ + NSArray *countryCodes = [NSLocale ISOCountryCodes]; + NSMutableArray *countries = [NSMutableArray arrayWithCapacity:[countryCodes count]]; + NSString *currentLanguage = [[NSLocale preferredLanguages] objectAtIndex:0]; + + for (NSString *countryCode in countryCodes) + { + NSString *identifier = [NSLocale localeIdentifierFromComponents: [NSDictionary dictionaryWithObject: countryCode forKey: NSLocaleCountryCode]]; + NSString *country = [[[NSLocale alloc] initWithLocaleIdentifier:currentLanguage] displayNameForKey: NSLocaleIdentifier value: identifier]; + [countries addObject: country]; + } + + self.codeForCountryDictionary = [[NSDictionary alloc] initWithObjects:countryCodes forKeys:countries]; + self.executeQueue = [NSOperationQueue new]; + self.executeQueue.maxConcurrentOperationCount = 6; + +} + +-(void)geocode:(CDVInvokedUrlCommand *)command +{ + [self.executeQueue addOperationWithBlock:^{ + NSDictionary *json = [command.arguments objectAtIndex:0]; + NSDictionary *position = [json objectForKey:@"position"]; + NSString *address = [json objectForKey:@"address"]; + int idx = 0; + + if ([json objectForKey:@"idx"]) { + idx = (int)[[json objectForKey:@"idx"] integerValue]; + } + + if (address && position == nil) { + __block CLGeocoder *geocoder = [CLGeocoder new]; + + NSArray *points = [json objectForKey:@"bounds"]; + + if (points) { + //center + int i = 0; + NSDictionary *latLng; + GMSMutablePath *path = [GMSMutablePath path]; + GMSCoordinateBounds *bounds; + for (i = 0; i < points.count; i++) { + latLng = [points objectAtIndex:i]; + [path addCoordinate:CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] floatValue], [[latLng objectForKey:@"lng"] floatValue])]; + } + bounds = [[GMSCoordinateBounds alloc] initWithPath:path]; + + + CLLocationCoordinate2D southWest = bounds.southWest; + CLLocationCoordinate2D northEast = bounds.northEast; + float latitude = (southWest.latitude + northEast.latitude) / 2.0; + float longitude = (southWest.longitude + northEast.longitude) / 2.0; + CLLocationCoordinate2D center = CLLocationCoordinate2DMake(latitude, longitude); + + //distance + CLLocation *locA = [[CLLocation alloc] initWithLatitude:center.latitude longitude:center.latitude]; + CLLocation *locB = [[CLLocation alloc] initWithLatitude:southWest.latitude longitude:southWest.longitude]; + CLLocationDistance distance = [locA distanceFromLocation:locB]; + + CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:distance/2 identifier:@"geocoder"]; + + + [geocoder geocodeAddressString:address inRegion:region completionHandler:^(NSArray *placemarks, NSError *error) { + + CDVPluginResult* pluginResult; + + if (error) { + if (placemarks.count == 0) { + NSMutableDictionary *resultJson = [[NSMutableDictionary alloc] init]; + [resultJson setObject:[NSNumber numberWithInt:idx] forKey:@"idx"]; + [resultJson setObject:[[NSArray alloc] init] forKey:@"results"]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resultJson]; + + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description]; + } + } else { + NSArray *results = [self geocoder_callback:placemarks error:error]; + NSMutableDictionary *resultJson = [[NSMutableDictionary alloc] init]; + [resultJson setObject:[NSNumber numberWithInt:idx] forKey:@"idx"]; + [resultJson setObject:results forKey:@"results"]; + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resultJson]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + } else { + //No region specified. + [geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) { + CDVPluginResult* pluginResult; + if (error) { + if (placemarks.count == 0) { + NSMutableDictionary *resultJson = [[NSMutableDictionary alloc] init]; + [resultJson setObject:[NSNumber numberWithInt:idx] forKey:@"idx"]; + [resultJson setObject:[[NSArray alloc] init] forKey:@"results"]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resultJson]; + + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description]; + } + } else { + NSArray *results = [self geocoder_callback:placemarks error:error]; + NSMutableDictionary *resultJson = [[NSMutableDictionary alloc] init]; + [resultJson setObject:[NSNumber numberWithInt:idx] forKey:@"idx"]; + [resultJson setObject:results forKey:@"results"]; + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resultJson]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + } + return; + } + + // Reverse geocoding + if (position && address == nil) { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + GMSGeocoder *reverseGeocoder = [GMSGeocoder geocoder]; + + NSDictionary *latLng = [json objectForKey:@"position"]; + CLLocationCoordinate2D position = CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] floatValue], [[latLng objectForKey:@"lng"] floatValue]); + + [reverseGeocoder reverseGeocodeCoordinate:position completionHandler:^(GMSReverseGeocodeResponse *response, NSError *error) { + + [self.executeQueue addOperationWithBlock:^{ + CDVPluginResult* pluginResult; + if (error) { + if ([response.results count] == 0) { + NSMutableDictionary *resultJson = [[NSMutableDictionary alloc] init]; + [resultJson setObject:[json objectForKey:@"idx"] forKey:@"idx"]; + [resultJson setObject:[[NSArray alloc] init] forKey:@"results"]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resultJson]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description]; + } + } else { + + NSMutableArray *results = [NSMutableArray array]; + GMSAddress *address; + NSString *countryCode; + int i; + for (i = 0; i < [response.results count]; i++) { + address = [response.results objectAtIndex:i]; + + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + [result setObject:[NSNumber numberWithDouble:address.coordinate.latitude] forKey:@"lat"]; + [result setObject:[NSNumber numberWithDouble:address.coordinate.longitude] forKey:@"lng"]; + + NSMutableDictionary *position = [NSMutableDictionary dictionary]; + [position setObject:[NSNumber numberWithDouble:address.coordinate.latitude] forKey:@"lat"]; + [position setObject:[NSNumber numberWithDouble:address.coordinate.longitude] forKey:@"lng"]; + [result setObject:position forKey:@"position"]; + + [result setObject:[NSString stringWithFormat:@"%@", address.locality] forKey:@"locality"]; + [result setObject:[NSString stringWithFormat:@"%@", address.administrativeArea] forKey:@"adminArea"]; + [result setObject:[NSString stringWithFormat:@"%@", address.country] forKey:@"country"]; + countryCode = [self.codeForCountryDictionary objectForKey:address.country]; + [result setObject:[NSString stringWithFormat:@"%@", countryCode] forKey:@"countryCode"]; + [result setObject:@"" forKey:@"locale"]; + [result setObject:[NSString stringWithFormat:@"%@", address.postalCode] forKey:@"postalCode"]; + [result setObject:@"" forKey:@"subAdminArea"]; + [result setObject:[NSString stringWithFormat:@"%@", address.subLocality] forKey:@"subLocality"]; + [result setObject:@"" forKey:@"subThoroughfare"]; + [result setObject:[NSString stringWithFormat:@"%@", address.thoroughfare] forKey:@"thoroughfare"]; + + + NSMutableDictionary *extra = [NSMutableDictionary dictionary]; + [extra setObject:address.lines forKey:@"lines"]; + [result setObject:extra forKey:@"extra"]; + + [results addObject:result]; + } + NSMutableDictionary *resultJson = [[NSMutableDictionary alloc] init]; + [resultJson setObject:[json objectForKey:@"idx"] forKey:@"idx"]; + [resultJson setObject:results forKey:@"results"]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resultJson]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + }]; + } + }]; +} + +- (NSArray *)geocoder_callback:(NSArray *)placemarks error:(NSError *)error +{ + NSMutableArray *results = [[NSMutableArray alloc] init]; + if ([placemarks count] > 0) { + + CLPlacemark *placemark; + CLLocation *location; + CLLocationCoordinate2D coordinate; + for (int i = 0; i < placemarks.count; i++) { + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + + NSMutableDictionary *position = [[NSMutableDictionary alloc] init]; + placemark = [placemarks objectAtIndex:i]; + location = placemark.location; + coordinate = location.coordinate; + [position setObject:[NSNumber numberWithDouble:coordinate.latitude] forKey:@"lat"]; + [position setObject:[NSNumber numberWithDouble:coordinate.longitude] forKey:@"lng"]; + [result setObject:position forKey:@"position"]; + + + if (placemark.administrativeArea) { + [result setObject:placemark.administrativeArea forKey:@"adminArea"]; + } + if (placemark.subAdministrativeArea) { + [result setObject:placemark.subAdministrativeArea forKey:@"subAdminArea"]; + } + + if (placemark.locality) { + [result setObject:placemark.locality forKey:@"locality"]; + } + + if (placemark.country) { + [result setObject:placemark.country forKey:@"country"]; + } + if (placemark.postalCode) { + [result setObject:placemark.postalCode forKey:@"postalCode"]; + } + if (placemark.subLocality) { + [result setObject:placemark.subLocality forKey:@"subLocality"]; + } + if (placemark.subThoroughfare) { + [result setObject:placemark.subThoroughfare forKey:@"subThoroughfare"]; + } + if (placemark.thoroughfare) { + [result setObject:placemark.thoroughfare forKey:@"thoroughfare"]; + } + + NSMutableDictionary *extra = [[NSMutableDictionary alloc] init]; + if (placemark.ocean) { + [extra setObject:placemark.ocean forKey:@"ocean"]; + } + if (placemark.addressDictionary) { + [extra setObject:placemark.addressDictionary forKey:@"address"]; + } + if (placemark.description) { + [extra setObject:placemark.description forKey:@"description"]; + } + if (placemark.inlandWater) { + [extra setObject:placemark.inlandWater forKey:@"inlandWater"]; + } + if (placemark.region) { + // iOS8 SDK still uses CLRegion inside CLPlaceMarker though, + // to supress the warning, cast to CLCircularRegion. + // http://www.4byte.cn/question/288833/deprecated-clregion-methods-how-to-get-radius.html + #ifdef __IPHONE_7_0 + CLCircularRegion *circularRegion = (CLCircularRegion *)placemark.region; + [extra setObject:[NSString stringWithFormat:@"%f", circularRegion.radius] forKey:@"radius"]; + #else + [extra setObject:[NSString stringWithFormat:@"%f", placemark.region.radius] forKey:@"radius"]; + #endif + + + [extra setObject:placemark.region.identifier forKey:@"identifier"]; + } + if (placemark.name) { + [extra setObject:placemark.name forKey:@"name"]; + } + [result setObject:extra forKey:@"extra"]; + + [results addObject:result]; + } + } + return results; + +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGroundOverlay.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGroundOverlay.h new file mode 100644 index 0000000..f2617bf --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGroundOverlay.h @@ -0,0 +1,30 @@ +// +// PluginGroundOverlay.h +// cordova-googlemaps-plugin v2 +// +// Created by Katsumata Masashi. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" +#import "PluginUtil.h" + +@interface PluginGroundOverlay : CDVPlugin +//@property (nonatomic, strong) NSCache* imgCache; +@property (nonatomic) BOOL initialized; + +@property (nonatomic, strong) PluginMapViewController* mapCtrl; +- (void)create:(CDVInvokedUrlCommand*)command; +- (void)remove:(CDVInvokedUrlCommand *)command; +- (void)setClickable:(CDVInvokedUrlCommand*)command; +- (void)setVisible:(CDVInvokedUrlCommand*)command; +- (void)setImage:(CDVInvokedUrlCommand*)command; +- (void)setBounds:(CDVInvokedUrlCommand*)command; +- (void)setOpacity:(CDVInvokedUrlCommand*)command; +- (void)setBearing:(CDVInvokedUrlCommand*)command; +- (void)setZIndex:(CDVInvokedUrlCommand *)command; +- (void)_setImage:(GMSGroundOverlay *)layer urlStr:(NSString *)urlStr completionHandler:(void (^)(BOOL succeeded))completionHandler; +- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock; + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGroundOverlay.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGroundOverlay.m new file mode 100644 index 0000000..10750a0 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginGroundOverlay.m @@ -0,0 +1,602 @@ +// +// PluginGroundOverlay.m +// cordova-googlemaps-plugin v2 +// +// Created by Katsumata Masashi. +// +// + +#import "PluginGroundOverlay.h" + +@implementation PluginGroundOverlay +- (void)pluginInitialize +{ + if (self.initialized) { + return; + } + [super pluginInitialize]; + // Initialize this plugin + //self.imgCache = [[NSCache alloc]init]; + //self.imgCache.totalCostLimit = 3 * 1024 * 1024 * 1024; // 3MB = Cache for image +} + +- (void)pluginUnload +{ + + + // Plugin destroy + NSArray *keys = [self.mapCtrl.objects allKeys]; + NSString *key; + for (int i = 0; i < [keys count]; i++) { + key = [keys objectAtIndex:i]; + if ([key hasPrefix:@"groundoverlay_property"]) { + key = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + GMSGroundOverlay *groundoverlay = (GMSGroundOverlay *)[self.mapCtrl.objects objectForKey:key]; + groundoverlay.map = nil; + groundoverlay = nil; + } + [self.mapCtrl.objects removeObjectForKey:key]; + } + + //[self.imgCache removeAllObjects]; + //self.imgCache = nil; + + key = nil; + keys = nil; + + NSString *pluginId = [NSString stringWithFormat:@"%@-groundoverlay", self.mapCtrl.overlayId]; + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + [cdvViewController.pluginObjects removeObjectForKey:pluginId]; + [cdvViewController.pluginsMap setValue:nil forKey:pluginId]; + pluginId = nil; +} + +-(void)setPluginViewController:(PluginViewController *)viewCtrl +{ + self.mapCtrl = (PluginMapViewController *)viewCtrl; +} + +-(void)create:(CDVInvokedUrlCommand *)command +{ + PluginGroundOverlay *self_ = self; + + NSDictionary *json = [command.arguments objectAtIndex:1]; + NSString *idBase = [command.arguments objectAtIndex:2]; + NSArray *points = [json objectForKey:@"bounds"]; + + GMSMutablePath *path = [GMSMutablePath path]; + GMSCoordinateBounds *bounds; + + if (points) { + //Generate a bounds + int i = 0; + NSDictionary *latLng; + for (i = 0; i < points.count; i++) { + latLng = [points objectAtIndex:i]; + [path addCoordinate:CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] floatValue], [[latLng objectForKey:@"lng"] floatValue])]; + } + } + bounds = [[GMSCoordinateBounds alloc] initWithPath:path]; + + dispatch_async(dispatch_get_main_queue(), ^{ + GMSGroundOverlay *groundOverlay = [GMSGroundOverlay groundOverlayWithBounds:bounds icon:nil]; + + NSString *groundOverlayId = [NSString stringWithFormat:@"groundoverlay_%@", idBase]; + [self.mapCtrl.objects setObject:groundOverlay forKey: groundOverlayId]; + groundOverlay.title = groundOverlayId; + groundOverlay.anchor = CGPointMake(0.5f, 0.5f); + + if ([json valueForKey:@"zIndex"] && [json valueForKey:@"zIndex"] != [NSNull null]) { + groundOverlay.zIndex = [[json valueForKey:@"zIndex"] floatValue]; + } + + if ([json valueForKey:@"bearing"] && [json valueForKey:@"bearing"] != [NSNull null]) { + groundOverlay.bearing = [[json valueForKey:@"bearing"] floatValue]; + } + if ([json valueForKey:@"anchor"] && [json valueForKey:@"anchor"] != [NSNull null]) { + NSArray *anchor = [json valueForKey:@"anchor"]; + groundOverlay.anchor = CGPointMake([[anchor objectAtIndex:0] floatValue], [[anchor objectAtIndex:1] floatValue]); + } + + BOOL isVisible = YES; + + // Visible property + NSString *visibleValue = [NSString stringWithFormat:@"%@", json[@"visible"]]; + if ([@"0" isEqualToString:visibleValue]) { + // false + isVisible = NO; + groundOverlay.map = nil; + } else { + // true or default + groundOverlay.map = self.mapCtrl.map; + } + BOOL isClickable = NO; + if ([json valueForKey:@"clickable"] != [NSNull null] && [[json valueForKey:@"clickable"] boolValue]) { + isClickable = YES; + } + + + // Since this plugin uses own touch-detection, + // set NO to the tappable property. + groundOverlay.tappable = NO; + + __block PluginGroundOverlay *me = self; + + // Load image + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *urlStr = [json objectForKey:@"url"]; + if (urlStr) { + [self _setImage:groundOverlay urlStr:urlStr completionHandler:^(BOOL successed) { + if (!successed) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + [self_.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + + //NSString *imgId = [NSString stringWithFormat:@"groundoverlay_image_%lu", (unsigned long)groundOverlay.hash]; + //[me.imgCache setObject:groundOverlay.icon forKey:imgId]; + + if ([json valueForKey:@"opacity"] && [json valueForKey:@"opacity"] != [NSNull null]) { + CGFloat opacity = [[json valueForKey:@"opacity"] floatValue]; + groundOverlay.icon = [groundOverlay.icon imageByApplyingAlpha:opacity]; + } + + + //--------------------------- + // Keep the properties + //--------------------------- + NSString *propertyId = [NSString stringWithFormat:@"groundoverlay_property_%@", idBase]; + + // points + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + // bounds (pre-calculate for click detection) + [properties setObject:groundOverlay.bounds forKey:@"bounds"]; + // isVisible + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"isVisible"]; + // isClickable + [properties setObject:[NSNumber numberWithBool:isClickable] forKey:@"isClickable"]; + // zIndex + [properties setObject:[NSNumber numberWithFloat:groundOverlay.zIndex] forKey:@"zIndex"];; + [me.mapCtrl.objects setObject:properties forKey:propertyId]; + + //--------------------------- + // Result for JS + //--------------------------- + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + [result setObject:groundOverlayId forKey:@"__pgmId"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self_.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + + }]; + } else { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Error: The url property is not specified."]; + [self_.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + }]; + }); + +} + +- (void)_setImage:(GMSGroundOverlay *)groundOverlay urlStr:(NSString *)urlStr completionHandler:(void (^)(BOOL succeeded))completionHandler { + + + NSRange range = [urlStr rangeOfString:@"http"]; + + if (range.location != 0) { + /** + * Load icon from file or Base64 encoded strings + */ + + UIImage *image; + if ([urlStr rangeOfString:@"data:image/"].location != NSNotFound && + [urlStr rangeOfString:@";base64,"].location != NSNotFound) { + + /** + * Base64 icon + */ + NSArray *tmp = [urlStr componentsSeparatedByString:@","]; + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:[tmp objectAtIndex:1] options:0]; + + image = [[UIImage alloc] initWithData:decodedData]; + + } else { + /** + * Load the icon from local path + */ + range = [urlStr rangeOfString:@"cdvfile://"]; + if (range.location != NSNotFound) { + + urlStr = [PluginUtil getAbsolutePathFromCDVFilePath:self.webView cdvFilePath:urlStr]; + + if (urlStr == nil) { + NSLog(@"(error)Can not convert '%@' to device full path.", urlStr); + completionHandler(NO); + return; + } + } + + range = [urlStr rangeOfString:@"://"]; + if (range.location == NSNotFound) { + + range = [urlStr rangeOfString:@"/"]; + if (range.location != 0) { + // Get the current URL, then calculate the relative path. + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + id webview = cdvViewController.webView; + dispatch_sync(dispatch_get_main_queue(), ^{ + NSURL *url = [webview URL]; + NSString *currentURL = url.absoluteString; + if (![[url lastPathComponent] isEqualToString:@"/"]) { + currentURL = [currentURL stringByDeletingLastPathComponent]; + } + + // remove page unchor (i.e index.html#page=test, index.html?key=value) + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[#\\?].*$" options:NSRegularExpressionCaseInsensitive error:nil]; + currentURL = [regex stringByReplacingMatchesInString:currentURL options:0 range:NSMakeRange(0, [currentURL length]) withTemplate:@""]; + + // remove file name (i.e /index.html) + regex = [NSRegularExpression regularExpressionWithPattern:@"\\/[^\\/]+\\.[^\\/]+$" options:NSRegularExpressionCaseInsensitive error:nil]; + currentURL = [regex stringByReplacingMatchesInString:currentURL options:0 range:NSMakeRange(0, [currentURL length]) withTemplate:@""]; + + + NSString *urlStr2 = [NSString stringWithFormat:@"%@/%@", currentURL, urlStr]; + urlStr2 = [urlStr2 stringByReplacingOccurrencesOfString:@":/" withString:@"://"]; + urlStr2 = [urlStr2 stringByReplacingOccurrencesOfString:@":///" withString:@"://"]; + url = [NSURL URLWithString:urlStr2]; + + [self downloadImageWithURL:url completionBlock:^(BOOL succeeded, UIImage *image) { + + if (!succeeded) { + completionHandler(NO); + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + groundOverlay.icon = image; + completionHandler(YES); + }); + + }]; + }); + return; + } else { + urlStr = [NSString stringWithFormat:@"file://%@", urlStr]; + } + } + + + range = [urlStr rangeOfString:@"file://"]; + if (range.location != NSNotFound) { + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"file://" withString:@""]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:urlStr]) { + NSLog(@"(error)There is no file at '%@'.", urlStr); + completionHandler(NO); + return; + } + } + + image = [UIImage imageNamed:urlStr]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + groundOverlay.icon = [UIImage imageNamed:urlStr]; + completionHandler(YES); + }); + + + } else { + NSURL *url = [NSURL URLWithString:urlStr]; + [self downloadImageWithURL:url completionBlock:^(BOOL succeeded, UIImage *image) { + + if (!succeeded) { + completionHandler(NO); + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + groundOverlay.icon = image; + completionHandler(YES); + }); + + }]; + } +} + +/** + * Remove the ground overlay + * @params key + */ +-(void)remove:(CDVInvokedUrlCommand *)command +{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *groundOverlayId = [command.arguments objectAtIndex:0]; + GMSGroundOverlay *groundOverlay = [self.mapCtrl.objects objectForKey:groundOverlayId]; + + //[self.imgCache removeObjectForKey:groundOverlayId]; + + NSString *propertyId = [groundOverlayId stringByReplacingOccurrencesOfString:@"groundoverlay_" withString:@"groundoverlay_property"]; + [self.mapCtrl.objects removeObjectForKey:propertyId]; + [self.mapCtrl.objects removeObjectForKey:groundOverlayId]; + + groundOverlay.map = nil; + groundOverlay = nil; + [self.mapCtrl.objects removeObjectForKey:groundOverlayId]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + + +/** + * Set visibility + * @params key + */ +-(void)setVisible:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *key = [command.arguments objectAtIndex:0]; + GMSGroundOverlay *groundOverlay = [self.mapCtrl.objects objectForKey:key]; + Boolean isVisible = [[command.arguments objectAtIndex:1] boolValue]; + + // Update the property + NSString *propertyId = [key stringByReplacingOccurrencesOfString:@"groundoverlay_" withString:@"groundoverlay_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"isVisible"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + if (isVisible) { + groundOverlay.map = self.mapCtrl.map; + } else { + groundOverlay.map = nil; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; +} + +/** + * set image + * @params key + */ +-(void)setImage:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *groundOverlayId = [command.arguments objectAtIndex:0]; + GMSGroundOverlay *groundOverlay = [self.mapCtrl.objects objectForKey:groundOverlayId]; + NSString *urlStr = [command.arguments objectAtIndex:1]; + if (urlStr) { + __block PluginGroundOverlay *self_ = self; + [self _setImage:groundOverlay urlStr:urlStr completionHandler:^(BOOL successed) { + CDVPluginResult* pluginResult; + if (successed) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + } + + [self_.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + } else { + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + + }]; + +} + +/** + * Set bounds + * @params key + */ +-(void)setBounds:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *groundOverlayId = [command.arguments objectAtIndex:0]; + GMSGroundOverlay *groundOverlay = [self.mapCtrl.objects objectForKey:groundOverlayId]; + GMSMutablePath *path = [GMSMutablePath path]; + + NSArray *points = [command.arguments objectAtIndex:1]; + int i = 0; + NSDictionary *latLng; + for (i = 0; i < points.count; i++) { + latLng = [points objectAtIndex:i]; + [path addCoordinate:CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] floatValue], [[latLng objectForKey:@"lng"] floatValue])]; + } + GMSCoordinateBounds *bounds; + bounds = [[GMSCoordinateBounds alloc] initWithPath:path]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [groundOverlay setBounds:bounds]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set opacity + * @params key + */ +-(void)setOpacity:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *groundOverlayId = [command.arguments objectAtIndex:0]; + GMSGroundOverlay *groundOverlay = [self.mapCtrl.objects objectForKey:groundOverlayId]; + CGFloat opacity = [[command.arguments objectAtIndex:1] floatValue]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + groundOverlay.opacity = opacity; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set bearing + * @params key + */ +-(void)setBearing:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *groundOverlayId = [command.arguments objectAtIndex:0]; + GMSGroundOverlay *groundOverlay = [self.mapCtrl.objects objectForKey:groundOverlayId]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + groundOverlay.bearing = [[command.arguments objectAtIndex:1] floatValue]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; + +} + + +/** + * Set clickable + * @params key + */ +-(void)setClickable:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *key = [command.arguments objectAtIndex:0]; + //GMSGroundOverlay *groundOverlay = (GMSGroundOverlay *)[self.mapCtrl.objects objectForKey:key]; + Boolean isClickable = [[command.arguments objectAtIndex:1] boolValue]; + + // Update the property + NSString *propertyId = [key stringByReplacingOccurrencesOfString:@"groundoverlay_" withString:@"groundoverlay_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:isClickable] forKey:@"isClickable"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +/** + * Set z-index + * @params key + */ +-(void)setZIndex:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *groundOverlayId = [command.arguments objectAtIndex:0]; + GMSGroundOverlay *groundOverlay = [self.mapCtrl.objects objectForKey:groundOverlayId]; + + NSInteger zIndex = [[command.arguments objectAtIndex:1] integerValue]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [groundOverlay setZIndex:(int)zIndex]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + + +- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *iconPath = url.absoluteString; + + // Since ionic local server declines HTTP access for some reason, + // replace URL with file path + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *wwwPath = [mainBundle pathForResource:@"www/cordova" ofType:@"js"]; + wwwPath = [wwwPath stringByReplacingOccurrencesOfString:@"/cordova.js" withString:@""]; + if ([iconPath containsString:@"assets/"]) { + iconPath = [iconPath regReplace:@"^.*assets/" replaceTxt:[NSString stringWithFormat:@"%@/assets/", wwwPath] options:NSRegularExpressionCaseInsensitive]; + } + // iconPath = [iconPath stringByReplacingOccurrencesOfString:wwwPath withString: @""]; + + // ionic 4 + iconPath = [iconPath stringByReplacingOccurrencesOfString:@"http://localhost:8080" withString: wwwPath]; + + // ionic 5 + iconPath = [iconPath stringByReplacingOccurrencesOfString:@"ionic://localhost" withString: wwwPath]; + + if ([iconPath hasPrefix:@"file://"] || [iconPath hasPrefix:@"/"]) { + iconPath = [iconPath stringByReplacingOccurrencesOfString:@"file://" withString:@""]; + if (![iconPath hasPrefix:@"/"]) { + iconPath = [NSString stringWithFormat:@"/%@", iconPath]; + } + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:iconPath]) { + //if (self.mapCtrl.debuggable) { + NSLog(@"(error)There is no file at '%@'.", iconPath); + //} + //[self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId]; + completionBlock(NO, nil); + return; + } + + UIImage *image = [UIImage imageNamed:iconPath]; + if (image) { + completionBlock(YES, image); + return; + } + } + + + + NSURLRequest *req = [NSURLRequest requestWithURL:url + cachePolicy:NSURLRequestReturnCacheDataElseLoad + timeoutInterval:5]; + NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:req]; + if (cachedResponse != nil) { + UIImage *image = [[UIImage alloc] initWithData:cachedResponse.data]; + if (image) { + completionBlock(YES, image); + return; + } + } + + + //------------------------------------------------------------- + // Use NSURLSessionDataTask instead of [NSURLConnection sendAsynchronousRequest] + // https://stackoverflow.com/a/20871647 + //------------------------------------------------------------- + NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; + NSURLSessionDataTask *getTask = [session dataTaskWithRequest:req + completionHandler:^(NSData *data, NSURLResponse *res, NSError *error) { + [session finishTasksAndInvalidate]; + + UIImage *image = [UIImage imageWithData:data]; + if (image) { + completionBlock(YES, image); + return; + } + + completionBlock(NO, nil); + + }]; + [getTask resume]; + + + }]; +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginKmlOverlay.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginKmlOverlay.h new file mode 100644 index 0000000..31a740a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginKmlOverlay.h @@ -0,0 +1,27 @@ +// +// PluginKmlOverlay.h +// cordova-googlemaps-plugin v2 +// +// Created by Katsumata Masashi. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" +#import "PluginUtil.h" +#import "TBXML.h" +#import "IPluginProtocol.h" + +@interface PluginKmlOverlay : CDVPlugin + +@property (nonatomic) BOOL initialized; +@property (nonatomic, strong) PluginMapViewController* mapCtrl; +- (void)create:(CDVInvokedUrlCommand*)command; +@end + +@interface KmlParseClass : NSObject +@property (nonatomic) NSMutableDictionary *styleHolder; +@property (nonatomic) NSMutableDictionary *schemaHolder; + +-(NSMutableDictionary *)parseXml:(TBXML *)tbxml rootElement:(TBXMLElement *)rootElement; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginKmlOverlay.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginKmlOverlay.m new file mode 100644 index 0000000..ec23cc3 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginKmlOverlay.m @@ -0,0 +1,358 @@ +// +// PluginKmlOverlay.m +// cordova-googlemaps-plugin v2 +// +// Created by Katsumata Masashi. +// +// + +#import "PluginKmlOverlay.h" + + +@implementation PluginKmlOverlay + +-(void)setPluginViewController:(PluginViewController *)viewCtrl +{ + self.mapCtrl = (PluginMapViewController *)viewCtrl; +} +- (void)pluginUnload +{ +} + +-(void)create:(CDVInvokedUrlCommand *)command +{ + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSDictionary *json = [command.arguments objectAtIndex:1]; + + NSError *error; + + NSString *urlStr = [json objectForKey:@"url"]; + NSRange range; + if (![urlStr hasPrefix:@"http"]) { + + if (![urlStr containsString:@"://"] && + ![urlStr hasPrefix:@"/"] && + ![urlStr hasPrefix:@"www"] && + ![urlStr hasPrefix:@"./"] && + ![urlStr hasPrefix:@"../"]) { + urlStr = [NSString stringWithFormat:@"./%@", urlStr]; + } + + if ([urlStr hasPrefix:@"./"] || [urlStr hasPrefix:@"../"]) { + NSError *error = nil; + + // replace repeated "./" (i.e ./././test.png) + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(\\.\\/)+" options:NSRegularExpressionCaseInsensitive error:&error]; + urlStr = [regex stringByReplacingMatchesInString:urlStr options:0 range:NSMakeRange(0, [urlStr length]) withTemplate:@"./"]; + + + // Get the current URL, then calculate the relative path. + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + + id webview = cdvViewController.webView; + NSString *currentURL; + NSURL *url = [webview URL]; + currentURL = url.absoluteString; + if (![[url lastPathComponent] isEqualToString:@"/"]) { + currentURL = [currentURL stringByReplacingOccurrencesOfString:[url lastPathComponent] withString:@""]; + } + // remove page unchor (i.e index.html#page=test, index.html?key=value) + regex = [NSRegularExpression regularExpressionWithPattern:@"[#\\?].*$" options:NSRegularExpressionCaseInsensitive error:&error]; + currentURL = [regex stringByReplacingMatchesInString:currentURL options:0 range:NSMakeRange(0, [currentURL length]) withTemplate:@""]; + + // remove file name (i.e /index.html) + regex = [NSRegularExpression regularExpressionWithPattern:@"\\/[^\\/]+\\.[^\\/]+$" options:NSRegularExpressionCaseInsensitive error:&error]; + currentURL = [regex stringByReplacingMatchesInString:currentURL options:0 range:NSMakeRange(0, [currentURL length]) withTemplate:@""]; + + if (![currentURL hasSuffix:@"/"]) { + currentURL = [NSString stringWithFormat:@"%@/", currentURL]; + } + urlStr = [NSString stringWithFormat:@"%@%@", currentURL, urlStr]; + + // remove file name (i.e /index.html) + regex = [NSRegularExpression regularExpressionWithPattern:@"(\\/\\.\\/+)+" options:NSRegularExpressionCaseInsensitive error:&error]; + urlStr = [regex stringByReplacingMatchesInString:urlStr options:0 range:NSMakeRange(0, [urlStr length]) withTemplate:@"/"]; + + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"%20" withString:@" "]; + + if (self.mapCtrl.debuggable) { + NSLog(@"urlStr = %@", urlStr); + } + } + range = [urlStr rangeOfString:@"cdvfile://"]; + if (range.location != NSNotFound) { + urlStr = [PluginUtil getAbsolutePathFromCDVFilePath:self.webView cdvFilePath:urlStr]; + if (urlStr == nil) { + NSMutableDictionary* details = [NSMutableDictionary dictionary]; + [details setValue:[NSString stringWithFormat:@"Can not convert '%@' to device full path.", urlStr] forKey:NSLocalizedDescriptionKey]; + error = [NSError errorWithDomain:@"world" code:200 userInfo:details]; + } + } + + range = [urlStr rangeOfString:@"file://"]; + if (range.location != NSNotFound) { + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"file://" withString:@""]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:urlStr]) { + NSMutableDictionary* details = [NSMutableDictionary dictionary]; + [details setValue:[NSString stringWithFormat:@"There is no file at '%@'.", urlStr] forKey:NSLocalizedDescriptionKey]; + error = [NSError errorWithDomain:@"world" code:200 userInfo:details]; + } + } + } + if (self.mapCtrl.debuggable) { + NSLog(@"urlStr = %@", urlStr); + } + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [self loadKml:urlStr completionBlock:^(BOOL succeeded, id result) { + CDVPluginResult* pluginResult; + if (succeeded) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:(NSDictionary *)result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error localizedDescription]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + }]; + + }]; + + }]; + +} + +- (void)loadKml:(NSString *)urlStr completionBlock:(void (^)(BOOL succeeded, id result))completionBlock { + urlStr = [urlStr stringByReplacingOccurrencesOfString:@" " withString:@"%20"]; + NSError *error; + TBXML *tbxml = [TBXML alloc];// initWithXMLFile:urlStr error:&error]; + + // Since ionic local server declines HTTP access for some reason, + // replace URL with file path + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *wwwPath = [mainBundle pathForResource:@"www/cordova" ofType:@"js"]; + wwwPath = [wwwPath stringByReplacingOccurrencesOfString:@"/cordova.js" withString:@""]; + + // urlStr = [urlStr stringByReplacingOccurrencesOfString:wwwPath withString: @""]; + + // ionic 4 + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"http://localhost:8080" withString: wwwPath]; + + // ionic 5 + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"ionic://localhost" withString: wwwPath]; + + + if ([urlStr hasPrefix:@"http://"] || [urlStr hasPrefix:@"https://"]) { + NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]]; + bool valid = [NSURLConnection canHandleRequest:req]; + if (valid) { + NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; + NSURLSessionDataTask *getTask = [session dataTaskWithRequest:req + completionHandler:^(NSData *data, NSURLResponse *res, NSError *error) { + [session finishTasksAndInvalidate]; + + if (error) { + NSMutableDictionary* details = [NSMutableDictionary dictionary]; + [details setValue:[NSString stringWithFormat:@"Cannot load KML data from %@", urlStr] forKey:NSLocalizedDescriptionKey]; + error = [NSError errorWithDomain:@"world" code:200 userInfo:details]; + completionBlock(NO, error); + return; + } + + TBXML *tbxml = [TBXML alloc]; + tbxml = [tbxml initWithXMLData:data error:&error]; + NSDictionary *result = [self parseXmlWithTbXml:tbxml]; + completionBlock(YES, result); + + }]; + [getTask resume]; + } else { + + NSMutableDictionary* details = [NSMutableDictionary dictionary]; + [details setValue:[NSString stringWithFormat:@"Cannot load KML data from %@", urlStr] forKey:NSLocalizedDescriptionKey]; + error = [NSError errorWithDomain:@"world" code:200 userInfo:details]; + completionBlock(NO, error); + } + } else { + tbxml = [tbxml initWithXMLFile:urlStr error:&error]; + NSDictionary *result = [self parseXmlWithTbXml:tbxml]; + completionBlock(YES, result); + } + + +} +- (NSDictionary *)parseXmlWithTbXml:(TBXML *)tbxml { + + KmlParseClass *parser = [[KmlParseClass alloc] init]; + NSMutableDictionary *root = [parser parseXml:tbxml rootElement:tbxml.rootXMLElement]; + + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + [result setObject:root forKey:@"root"]; + [result setObject:parser.schemaHolder forKey:@"schemas"]; + [result setObject:parser.styleHolder forKey:@"styles"]; + return result; +} +@end + + +//------------------------------------------ +// @private +// KmlParseClass +//------------------------------------------ +@implementation KmlParseClass + +-(instancetype)init { + self = [super init]; + self.styleHolder = [NSMutableDictionary dictionary]; + self.schemaHolder = [NSMutableDictionary dictionary]; + return self; +} + +-(NSMutableDictionary *)parseXml:(TBXML *)tbxml rootElement:(TBXMLElement *)rootElement +{ + + if (rootElement == nil) { + return nil; + } + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + NSString *tagName = [[TBXML elementName:rootElement] lowercaseString]; + NSString *styleId, *schemaId, *txt; + + //NSLog(@"---> tagName = %@", tagName); + [result setObject:tagName forKey:@"tagName"]; + + NSString *attrName, *value; + TBXMLAttribute *attribute = rootElement->firstAttribute; + while (attribute) { + attrName = [TBXML attributeName:attribute]; + value = [TBXML attributeValue:attribute]; + [result setObject:value forKey:attrName]; + attribute = attribute->next; + } + + if ([@"styleurl" isEqualToString:tagName]) { + styleId = [TBXML textForElement:rootElement]; + [result setObject:styleId forKey:@"styleId"]; + return result; + + } + + + if ([@"style" isEqualToString:tagName] || [@"stylemap" isEqualToString:tagName]) { + // Generate a style id for the tag + styleId = [TBXML valueOfAttributeNamed:@"id" forElement:rootElement]; + if (styleId == nil || [styleId isEqualToString:@""]) { + styleId = [NSString stringWithFormat:@"__%ld%d__", tagName.hash, rand() * RAND_MAX]; + } + [result setObject:styleId forKey:@"styleId"]; + + // Store style information into the styleHolder. + NSMutableDictionary *styles = [NSMutableDictionary dictionary]; + NSMutableArray *children = [NSMutableArray array]; + TBXMLElement *childNode = rootElement->firstChild; + while (childNode) { + + NSMutableDictionary *node = [self parseXml:tbxml rootElement:childNode]; + if (node) { + if ([node objectForKey:@"value"]) { + [result setObject:[node objectForKey:@"value"] forKey:[node objectForKey:@"tagName"]]; + } else { + [children addObject:node]; + } + } + childNode = childNode->nextSibling; + } + if ([children count] > 0) { + [styles setObject:children forKey:@"children"]; + } + [self.styleHolder setObject:styles forKey:styleId]; + return result; + + } + + + if ([@"schema" isEqualToString:tagName]) { + // Generate a schema id for the tag + schemaId = [TBXML valueOfAttributeNamed:@"id" forElement:rootElement]; + if (schemaId == nil || [schemaId isEqualToString:@""]) { + schemaId = [NSString stringWithFormat:@"__%ld%d__", tagName.hash, rand() * RAND_MAX]; + } + + // Store schema information into the schemaHolder. + NSMutableDictionary *schema = [NSMutableDictionary dictionary]; + [schema setObject:[TBXML valueOfAttributeNamed:@"name" forElement:rootElement] forKey:@"name"]; + + NSMutableArray *children = [NSMutableArray array]; + TBXMLElement *childNode = rootElement->firstChild; + while (childNode) { + + NSMutableDictionary *node = [self parseXml:tbxml rootElement:childNode]; + if (node) { + [children addObject:node]; + } + childNode = childNode->nextSibling; + } + if ([children count] > 0) { + [schema setObject:children forKey:@"children"]; + } + [self.schemaHolder setObject:schema forKey:schemaId]; + return result; + } + + if ([@"coordinates" isEqualToString:tagName]) { + NSMutableArray *latLngList = [NSMutableArray array]; + txt = [TBXML textForElement:rootElement]; + txt = [txt regReplace:@"\\s+" replaceTxt:@"\n" options:0]; + txt = [txt regReplace:@"\\n+" replaceTxt:@"\n" options:0]; + NSArray *lines = [txt componentsSeparatedByString:@"\n"]; + NSArray *tmpArry; + NSMutableDictionary *latLng; + NSString *tmp; + for (int i = 0; i < lines.count; i++) { + tmp = [lines objectAtIndex:i]; + tmp = [tmp regReplace:@"[^0-9,.eE\\-]" replaceTxt:@"" options:0]; + if ([tmp isEqualToString:@""] == NO) { + tmpArry = [tmp componentsSeparatedByString:@","]; + latLng = [NSMutableDictionary dictionary]; + [latLng setObject:[tmpArry objectAtIndex:1] forKey:@"lat"]; + [latLng setObject:[tmpArry objectAtIndex:0] forKey:@"lng"]; + [latLngList addObject:latLng]; + } + } + + [result setObject:latLngList forKey:tagName]; + return result; + } + + + TBXMLElement *childNode = rootElement->firstChild; + NSMutableArray *styleIDs; + if (childNode) { + NSMutableArray *children = [NSMutableArray array]; + while(childNode) { + NSMutableDictionary *node = [self parseXml:tbxml rootElement:childNode]; + if (node) { + if ([node objectForKey:@"styleId"]) { + styleIDs = [result objectForKey:@"styleIDs"]; + if (styleIDs == nil) { + styleIDs = [NSMutableArray array]; + } + [styleIDs addObject:[node objectForKey:@"styleId"]]; + [result setObject:styleIDs forKey:@"styleIDs"]; + } else if ([@"schema" isEqualToString:[node objectForKey:@"tagName"]] == NO) { + [children addObject:node]; + } + } + childNode = childNode->nextSibling; + } + [result setObject:children forKey:@"children"]; + } else { + [result setObject:[TBXML textForElement:rootElement] forKey:@"value"]; + } + return result; +} + + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginLocationService.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginLocationService.h new file mode 100644 index 0000000..ae84a87 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginLocationService.h @@ -0,0 +1,21 @@ +// +// PluginLocationService.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import +#import +#import "PluginUtil.h" + +@interface PluginLocationService : CDVPlugin + +@property (nonatomic, strong) NSMutableDictionary *lastResult; +@property (nonatomic, strong) CLLocation *lastLocation; +@property (nonatomic, strong) CLLocationManager *locationManager; +@property (nonatomic, strong) NSMutableArray *locationCommandQueue; +- (void)getMyLocation:(CDVInvokedUrlCommand*)command; +- (void)hasPermission:(CDVInvokedUrlCommand*)command; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginLocationService.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginLocationService.m new file mode 100644 index 0000000..841da3e --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginLocationService.m @@ -0,0 +1,233 @@ +// +// PluginLocationService.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginLocationService.h" + +@implementation PluginLocationService + +- (void)pluginInitialize +{ + self.locationCommandQueue = [[NSMutableArray alloc] init]; + self.lastLocation = nil; +} + +/** + * Return 1 if the app has geolocation permission + */ +- (void)hasPermission:(CDVInvokedUrlCommand*)command { + + int result = 1; + CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; + if (status == kCLAuthorizationStatusDenied || + status == kCLAuthorizationStatusRestricted) { + result = 0; + } + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + +} +/** + * Return the current position based on GPS + */ +-(void)getMyLocation:(CDVInvokedUrlCommand *)command +{ + dispatch_async(dispatch_get_main_queue(), ^{ + // Obtain the authorizationStatus + CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; + if (status == kCLAuthorizationStatusDenied || + status == kCLAuthorizationStatusRestricted) { + //---------------------------------------------------- + // kCLAuthorizationStatusDenied + // kCLAuthorizationStatusRestricted + //---------------------------------------------------- + + NSString *error_code = @"service_denied"; + NSString *error_message = [PluginUtil PGM_LOCALIZATION:@"LOCATION_IS_DENIED_MESSAGE"]; + + NSMutableDictionary *json = [NSMutableDictionary dictionary]; + [json setObject:[NSNumber numberWithBool:NO] forKey:@"status"]; + [json setObject:[NSString stringWithString:error_message] forKey:@"error_message"]; + [json setObject:[NSString stringWithString:error_code] forKey:@"error_code"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:json]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +// +// NSString *LOCATION_IS_UNAVAILABLE_ERROR_TITLE = [PluginUtil PGM_LOCALIZATION:@"LOCATION_IS_UNAVAILABLE_ERROR_TITLE"]; +// NSString *LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE = [PluginUtil PGM_LOCALIZATION:@"LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE"]; +// UIAlertController* alert = [UIAlertController alertControllerWithTitle:LOCATION_IS_UNAVAILABLE_ERROR_TITLE +// message:LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE +// preferredStyle:UIAlertControllerStyleAlert]; +// +// NSString *closeBtnLabel = [PluginUtil PGM_LOCALIZATION:@"CLOSE_BUTTON"]; +// UIAlertAction* ok = [UIAlertAction actionWithTitle:closeBtnLabel +// style:UIAlertActionStyleDefault +// handler:^(UIAlertAction* action) +// { +// NSString *error_code = @"service_denied"; +// NSString *error_message = [PluginUtil PGM_LOCALIZATION:@"LOCATION_IS_DENIED_MESSAGE"]; +// +// NSMutableDictionary *json = [NSMutableDictionary dictionary]; +// [json setObject:[NSNumber numberWithBool:NO] forKey:@"status"]; +// [json setObject:[NSString stringWithString:error_message] forKey:@"error_message"]; +// [json setObject:[NSString stringWithString:error_code] forKey:@"error_code"]; +// +// CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:json]; +// [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +// [alert dismissViewControllerAnimated:YES completion:nil]; +// }]; +// +// [alert addAction:ok]; +// +// +// [self.viewController presentViewController:alert +// animated:YES +// completion:nil]; + + } else { + + if (self.locationManager == nil) { + self.locationManager = [[CLLocationManager alloc] init]; + } + self.locationManager.delegate = self; + + + //---------------------------------------------------- + // kCLAuthorizationStatusNotDetermined + // kCLAuthorizationStatusAuthorized + // kCLAuthorizationStatusAuthorizedAlways + // kCLAuthorizationStatusAuthorizedWhenInUse + //---------------------------------------------------- + CLLocationAccuracy locationAccuracy = kCLLocationAccuracyNearestTenMeters; + NSDictionary *opts = [command.arguments objectAtIndex:0]; + BOOL isEnabledHighAccuracy = NO; + if ([opts objectForKey:@"enableHighAccuracy"]) { + isEnabledHighAccuracy = [[opts objectForKey:@"enableHighAccuracy"] boolValue]; + } + + if (isEnabledHighAccuracy == YES) { + locationAccuracy = kCLLocationAccuracyBestForNavigation; + self.locationManager.distanceFilter = 5; + } else { + self.locationManager.distanceFilter = 10; + } + self.locationManager.desiredAccuracy = locationAccuracy; + + //http://stackoverflow.com/questions/24268070/ignore-ios8-code-in-xcode-5-compilation + [self.locationManager requestWhenInUseAuthorization]; + + if (self.lastLocation && -[self.lastLocation.timestamp timeIntervalSinceNow] < 2) { + //--------------------------------------------------------------------- + // If the user requests the location in two seconds from the last time, + // return the last result in order to save battery usage. + // (Don't request the device location too much! Save battery usage!) + //--------------------------------------------------------------------- + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:self.lastResult]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + + if (self.locationCommandQueue.count == 0) { + // Executes getMyLocation() first time + + [self.locationManager stopUpdatingLocation]; + + // Why do I have to still support iOS9? + [NSTimer scheduledTimerWithTimeInterval:6000 + target:self + selector:@selector(locationFailed) + userInfo:nil + repeats:NO]; + [self.locationManager startUpdatingLocation]; + } + [self.locationCommandQueue addObject:command]; + + //CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + //[pluginResult setKeepCallbackAsBool:YES]; + //[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + }); +} + +-(void)locationFailed +{ + if (self.lastLocation != nil) { + return; + } + + // Timeout + [self.locationManager stopUpdatingLocation]; + + NSMutableDictionary *json = [NSMutableDictionary dictionary]; + [json setObject:[NSNumber numberWithBool:NO] forKey:@"status"]; + NSString *error_code = @"error"; + NSString *error_message = [PluginUtil PGM_LOCALIZATION:@"CAN_NOT_GET_LOCATION_MESSAGE"]; + [json setObject:[NSString stringWithString:error_message] forKey:@"error_message"]; + [json setObject:[NSString stringWithString:error_code] forKey:@"error_code"]; + + for (CDVInvokedUrlCommand *command in self.locationCommandQueue) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:json]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + [self.locationCommandQueue removeAllObjects]; +} + +-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { + self.lastLocation = self.locationManager.location; + + NSMutableDictionary *latLng = [NSMutableDictionary dictionary]; + [latLng setObject:[NSNumber numberWithDouble:self.locationManager.location.coordinate.latitude] forKey:@"lat"]; + [latLng setObject:[NSNumber numberWithDouble:self.locationManager.location.coordinate.longitude] forKey:@"lng"]; + + NSMutableDictionary *json = [NSMutableDictionary dictionary]; + [json setObject:[NSNumber numberWithBool:YES] forKey:@"status"]; + + [json setObject:latLng forKey:@"latLng"]; + [json setObject:[NSNumber numberWithFloat:[self.locationManager.location speed]] forKey:@"speed"]; + [json setObject:[NSNumber numberWithFloat:[self.locationManager.location altitude]] forKey:@"altitude"]; + + //todo: calcurate the correct accuracy based on horizontalAccuracy and verticalAccuracy + [json setObject:[NSNumber numberWithFloat:[self.locationManager.location horizontalAccuracy]] forKey:@"accuracy"]; + [json setObject:[NSNumber numberWithDouble:[self.locationManager.location.timestamp timeIntervalSince1970]] forKey:@"time"]; + [json setObject:[NSNumber numberWithInteger:[self.locationManager.location hash]] forKey:@"hashCode"]; + self.lastResult = json; + + for (CDVInvokedUrlCommand *command in self.locationCommandQueue) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:json]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + + [self.locationCommandQueue removeAllObjects]; + [self.locationManager stopUpdatingLocation]; + //self.locationManager.delegate = nil; + //self.locationManager = nil; +} +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { + self.lastLocation = nil; + self.lastResult = nil; + + NSMutableDictionary *json = [NSMutableDictionary dictionary]; + [json setObject:[NSNumber numberWithBool:NO] forKey:@"status"]; + NSString *error_code = @"error"; + NSString *error_message = [PluginUtil PGM_LOCALIZATION:@"CAN_NOT_GET_LOCATION_MESSAGE"]; + if (error.code == kCLErrorDenied) { + error_code = @"service_denied"; + error_message = [PluginUtil PGM_LOCALIZATION:@"LOCATION_REJECTED_BY_USER_MESSAGE"]; + } + + [json setObject:[NSString stringWithString:error_message] forKey:@"error_message"]; + [json setObject:[NSString stringWithString:error_code] forKey:@"error_code"]; + + for (CDVInvokedUrlCommand *command in self.locationCommandQueue) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:json]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + [self.locationCommandQueue removeAllObjects]; + +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMap.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMap.h new file mode 100644 index 0000000..53785c0 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMap.h @@ -0,0 +1,46 @@ +// +// PluginMap.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" +#import "IPluginView.h" + +@interface PluginMap : CDVPlugin +@property (nonatomic, strong) PluginMapViewController* mapCtrl; +@property (nonatomic) BOOL isRemoved; +@property (nonatomic) BOOL initialized; + +- (void)clear:(CDVInvokedUrlCommand*)command; +- (void)setClickable:(CDVInvokedUrlCommand*)command; +- (void)setVisible:(CDVInvokedUrlCommand*)command; +- (void)setCameraTilt:(CDVInvokedUrlCommand*)command; +- (void)setCameraTarget:(CDVInvokedUrlCommand*)command; +- (void)setCameraBearing:(CDVInvokedUrlCommand *)command; +- (void)setCameraZoom:(CDVInvokedUrlCommand*)command; +- (void)setDiv:(CDVInvokedUrlCommand *)command; +- (void)setMapTypeId:(CDVInvokedUrlCommand*)command; +- (void)animateCamera:(CDVInvokedUrlCommand*)command; +- (void)loadPlugin:(CDVInvokedUrlCommand*)command; +- (void)getMap:(CDVInvokedUrlCommand*)command; +- (void)moveCamera:(CDVInvokedUrlCommand*)command; +- (void)setMyLocationEnabled:(CDVInvokedUrlCommand*)command; +- (void)setIndoorEnabled:(CDVInvokedUrlCommand*)command; +- (void)setTrafficEnabled:(CDVInvokedUrlCommand*)command; +- (void)setCompassEnabled:(CDVInvokedUrlCommand*)command; +- (void)attachToWebView:(CDVInvokedUrlCommand*)command; +- (void)detachFromWebView:(CDVInvokedUrlCommand*)command; +- (void)toDataURL:(CDVInvokedUrlCommand*)command; +- (void)setOptions:(CDVInvokedUrlCommand*)command; +- (void)setAllGesturesEnabled:(CDVInvokedUrlCommand*)command; +- (void)setPadding:(CDVInvokedUrlCommand*)command; +- (void)panBy:(CDVInvokedUrlCommand*)command; +- (void)getFocusedBuilding:(CDVInvokedUrlCommand*)command; +- (void)setActiveMarkerId:(CDVInvokedUrlCommand*)command; +- (void)stopAnimation:(CDVInvokedUrlCommand*)command; + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMap.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMap.m new file mode 100644 index 0000000..31605c9 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMap.m @@ -0,0 +1,1008 @@ +// +// PluginMap.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginMap.h" + +@implementation PluginMap + +-(void)setPluginViewController:(PluginViewController *)viewCtrl +{ + self.mapCtrl = (PluginMapViewController *)viewCtrl; +} + +- (void)pluginInitialize +{ + if (self.mapCtrl.objects) { + return; + } + self.initialized = YES; + // Initialize this plugin +} + +- (void)pluginUnload +{ + // Plugin destroy + self.isRemoved = YES; + + // Load the GoogleMap.m + //CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + //CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + + [self clear:nil]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self.mapCtrl.map removeFromSuperview]; + [self.mapCtrl.view removeFromSuperview]; + [self.mapCtrl.view setFrame:CGRectMake(0, -1000, 100, 100)]; + [self.mapCtrl.view setNeedsDisplay]; + + NSArray *keys = [self.mapCtrl.plugins allKeys]; + CDVPlugin *plugin; + for (int i = 0; i < [keys count]; i++) { + plugin = [self.mapCtrl.plugins objectForKey:[keys objectAtIndex:i]]; + [plugin pluginUnload]; + plugin = nil; + } + + + [self.mapCtrl.plugins removeAllObjects]; + self.mapCtrl.map = nil; + self.mapCtrl = nil; + }); + +} +- (void)loadPlugin:(CDVInvokedUrlCommand*)command { + NSString *className = [command.arguments objectAtIndex:0]; + NSString *pluginName = [NSString stringWithFormat:@"%@", className]; + className = [NSString stringWithFormat:@"Plugin%@", className]; + + @synchronized (self.mapCtrl.plugins) { + + CDVPluginResult* pluginResult = nil; + CDVPlugin *plugin; + NSString *pluginId = [NSString stringWithFormat:@"%@-%@", self.mapCtrl.overlayId, [pluginName lowercaseString]]; + + plugin = [self.mapCtrl.plugins objectForKey:pluginId]; + if (!plugin) { + plugin = [[NSClassFromString(className)alloc] init]; + + if (!plugin) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR + messageAsString:[NSString stringWithFormat:@"Class not found: %@", className]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + + // Hack: + // In order to load the plugin instance of the same class but different names, + // register the map plugin instance into the pluginObjects directly. + // + // Don't use the registerPlugin() method of the CDVViewController. + // Problem is at + // https://github.com/apache/cordova-ios/blob/582e35776f01ee03f32f0986de181bcf5eb4d232/CordovaLib/Classes/Public/CDVViewController.m#L577 + // + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + if ([plugin respondsToSelector:@selector(setViewController:)]) { + [plugin setViewController:cdvViewController]; + } + if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) { + [plugin setCommandDelegate:cdvViewController.commandDelegate]; + } + [cdvViewController.pluginObjects setObject:plugin forKey:pluginId]; + [cdvViewController.pluginsMap setValue:pluginId forKey:pluginId]; + [plugin pluginInitialize]; + + //NSLog(@"--->loadPlugin : %@ className : %@, plugin : %@", pluginId, className, plugin); + [self.mapCtrl.plugins setObject:plugin forKey:pluginId]; + [plugin setPluginViewController:self.mapCtrl]; + + } + + //plugin.commandDelegate = self.commandDelegate; + + + SEL selector = NSSelectorFromString(@"create:"); + if ([plugin respondsToSelector:selector]){ + [plugin performSelectorInBackground:selector withObject:command]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR + messageAsString:[NSString stringWithFormat:@"method not found: %@ in %@ class", @"create", className]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + } +} + +- (void)getMap:(CDVInvokedUrlCommand*)command { + + + if ([command.arguments count] == 1) { + //----------------------------------------------------------------------- + // case: plugin.google.maps.getMap([options]) (no the mapDiv is given) + //----------------------------------------------------------------------- + [self setOptions:command]; + //[self.mapCtrl.view setHidden:true]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + + return; + } + NSDictionary *meta = [command.arguments objectAtIndex:0]; + self.mapCtrl.viewDepth = [[meta objectForKey:@"depth"] integerValue]; + + NSDictionary *initOptions = [command.arguments objectAtIndex:1]; + if ([initOptions valueForKey:@"camera"] && [initOptions valueForKey:@"camera"] != [NSNull null]) { + double delayInSeconds = 1; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + [self _setOptions:initOptions requestMethod:@"getMap" command:command]; + }); + } else { + [self _setOptions:initOptions requestMethod:@"getMap" command:command]; + } + +} + + +- (void)setDiv:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + // Load the GoogleMap.m + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + + // Detach the map view + if ([command.arguments count] == 0) { + [googlemaps.pluginLayer removePluginOverlay:self.mapCtrl]; + self.mapCtrl.attached = NO; + self.mapCtrl.view = nil; + } else { + self.mapCtrl.view = self.mapCtrl.map; + [googlemaps.pluginLayer addPluginOverlay:self.mapCtrl]; + NSString *mapDivId = [command.arguments objectAtIndex:0]; + self.mapCtrl.divId = mapDivId; + self.mapCtrl.attached = YES; + self.mapCtrl.isRenderedAtOnce = NO; //prevent unexpected animation + [googlemaps.pluginLayer updateViewPosition:self.mapCtrl]; + } + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)attachToWebView:(CDVInvokedUrlCommand*)command { + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + // Load the GoogleMap.m + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + [googlemaps.pluginLayer addPluginOverlay:self.mapCtrl]; + self.mapCtrl.attached = YES; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)detachFromWebView:(CDVInvokedUrlCommand*)command { + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + // Load the GoogleMap.m + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + [googlemaps.pluginLayer removePluginOverlay:self.mapCtrl]; + self.mapCtrl.attached = NO; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + +} + +- (void)resizeMap:(CDVInvokedUrlCommand *)command { + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *mapDivId = self.mapCtrl.divId; + if (!mapDivId) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + + // Load the GoogleMap.m + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + + // Save the map rectangle. + if (![googlemaps.pluginLayer.pluginScrollView.HTMLNodes objectForKey:self.mapCtrl.divId]) { + NSMutableDictionary *dummyInfo = [[NSMutableDictionary alloc] init];; + [dummyInfo setObject:@"{{0,-3000} - {50,50}}" forKey:@"size"]; + [dummyInfo setObject:[NSNumber numberWithDouble:-999] forKey:@"depth"]; + [googlemaps.pluginLayer.pluginScrollView.HTMLNodes setObject:dummyInfo forKey:self.mapCtrl.divId]; + } + + + dispatch_async(dispatch_get_main_queue(), ^{ + [googlemaps.pluginLayer updateViewPosition:self.mapCtrl]; + + //[googlemaps.pluginLayer updateViewPosition:self.mapCtrl]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }); + + }]; +} + +-(void)setClickable:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + Boolean isClickable = [[command.arguments objectAtIndex:0] boolValue]; + self.mapCtrl.clickable = isClickable; + //self.debugView.clickable = isClickable; + //[self.pluginScrollView.debugView setNeedsDisplay]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +/** + * Clear all markups + */ +- (void)clear:(CDVInvokedUrlCommand *)command { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.mapCtrl.map clear]; + }); + + + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CDVPlugin *plugin; + NSString *pluginName; + NSArray *keys = [self.mapCtrl.plugins allKeys]; + for (int j = 0; j < [keys count]; j++) { + pluginName = [keys objectAtIndex:j]; + plugin = [self.mapCtrl.plugins objectForKey:pluginName]; + [plugin pluginUnload]; + + [cdvViewController.pluginObjects removeObjectForKey:pluginName]; + [cdvViewController.pluginsMap setValue:nil forKey:pluginName]; + //plugin = nil; + } + + [self.mapCtrl.plugins removeAllObjects]; + + if (command != nil) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + + } +} + +/** + * Move the center of the map + */ +- (void)setCameraTarget:(CDVInvokedUrlCommand *)command { + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + float latitude = [[command.arguments objectAtIndex:0] floatValue]; + float longitude = [[command.arguments objectAtIndex:1] floatValue]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [self.mapCtrl.map animateToLocation:CLLocationCoordinate2DMake(latitude, longitude)]; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)setMyLocationEnabled:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSDictionary *params =[command.arguments objectAtIndex:0]; + + self.mapCtrl.map.settings.myLocationButton = [[params valueForKey:@"myLocationButton"] boolValue]; + self.mapCtrl.map.myLocationEnabled = [[params valueForKey:@"myLocation"] boolValue]; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)setIndoorEnabled:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean isEnabled = [[command.arguments objectAtIndex:0] boolValue]; + self.mapCtrl.map.settings.indoorPicker = isEnabled; + self.mapCtrl.map.indoorEnabled = isEnabled; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)setTrafficEnabled:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean isEnabled = [[command.arguments objectAtIndex:0] boolValue]; + self.mapCtrl.map.trafficEnabled = isEnabled; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)setCompassEnabled:(CDVInvokedUrlCommand *)command { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean isEnabled = [[command.arguments objectAtIndex:0] boolValue]; + self.mapCtrl.map.settings.compassButton = isEnabled; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} +- (void)setVisible:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + BOOL isVisible = [[command.arguments objectAtIndex:0] boolValue]; + [self.mapCtrl.view setHidden:!isVisible]; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)setCameraTilt:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + double angle = [[command.arguments objectAtIndex:0] doubleValue]; + if (angle >=0 && angle <= 90) { + GMSCameraPosition *camera = self.mapCtrl.map.camera; + camera = [GMSCameraPosition cameraWithLatitude:camera.target.latitude + longitude:camera.target.longitude + zoom:camera.zoom + bearing:camera.bearing + viewingAngle:angle]; + + [self.mapCtrl.map setCamera:camera]; + } + + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)setCameraBearing:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + double bearing = [[command.arguments objectAtIndex:0] doubleValue]; + GMSCameraPosition *camera = self.mapCtrl.map.camera; + camera = [GMSCameraPosition cameraWithLatitude:camera.target.latitude + longitude:camera.target.longitude + zoom:camera.zoom + bearing:bearing + viewingAngle:camera.viewingAngle]; + + + [self.mapCtrl.map setCamera:camera]; + + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} +- (void)setAllGesturesEnabled:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean isEnabled = [[command.arguments objectAtIndex:0] boolValue]; + [self.mapCtrl.map.settings setAllGesturesEnabled:isEnabled]; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + + +/** + * Change the zoom level + */ +- (void)setCameraZoom:(CDVInvokedUrlCommand *)command { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + float zoom = [[command.arguments objectAtIndex:0] floatValue]; + CLLocationCoordinate2D center = [self.mapCtrl.map.projection coordinateForPoint:self.mapCtrl.map.center]; + [self.mapCtrl.map setCamera:[GMSCameraPosition cameraWithTarget:center zoom:zoom]]; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +/** + * Pan by + */ +- (void)panBy:(CDVInvokedUrlCommand *)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + int x = [[command.arguments objectAtIndex:0] intValue]; + int y = [[command.arguments objectAtIndex:1] intValue]; + [self.mapCtrl.map animateWithCameraUpdate:[GMSCameraUpdate scrollByX:x * -1 Y:y * -1]]; + }]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +/** + * Change the Map Type + */ +- (void)setMapTypeId:(CDVInvokedUrlCommand *)command { + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + CDVPluginResult* pluginResult = nil; + + NSString *typeStr = [command.arguments objectAtIndex:0]; + NSDictionary *mapTypes = [NSDictionary dictionaryWithObjectsAndKeys: + ^() {return kGMSTypeHybrid; }, @"MAP_TYPE_HYBRID", + ^() {return kGMSTypeSatellite; }, @"MAP_TYPE_SATELLITE", + ^() {return kGMSTypeTerrain; }, @"MAP_TYPE_TERRAIN", + ^() {return kGMSTypeNormal; }, @"MAP_TYPE_NORMAL", + ^() {return kGMSTypeNone; }, @"MAP_TYPE_NONE", + nil]; + + typedef GMSMapViewType (^CaseBlock)(); + GMSMapViewType mapType; + CaseBlock caseBlock = mapTypes[typeStr]; + if (caseBlock) { + // Change the map type + mapType = caseBlock(); + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + self.mapCtrl.map.mapType = mapType; + }]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + // Error : User specifies unknow map type id + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR + messageAsString:[NSString + stringWithFormat:@"Unknow MapTypeID is specified:%@", typeStr]]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +/** + * Move the map camera with animation + */ +-(void)animateCamera:(CDVInvokedUrlCommand *)command +{ + [self updateCameraPosition:@"animateCamera" command:command]; +} + +/** + * Move the map camera + */ +-(void)moveCamera:(CDVInvokedUrlCommand *)command +{ + [self updateCameraPosition:@"moveCamera" command:command]; +} + + +-(void)_changeCameraPosition: (NSString*)action requestMethod:(NSString *)requestMethod params:(NSDictionary *)json command:(CDVInvokedUrlCommand *)command { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + __block double bearing; + if ([json valueForKey:@"bearing"] && [json valueForKey:@"bearing"] != [NSNull null]) { + bearing = [[json valueForKey:@"bearing"] doubleValue]; + } else { + bearing = self.mapCtrl.map.camera.bearing; + } + + double angle; + if ([json valueForKey:@"tilt"] && [json valueForKey:@"tilt"] != [NSNull null]) { + angle = [[json valueForKey:@"tilt"] doubleValue]; + } else { + angle = self.mapCtrl.map.camera.viewingAngle; + } + + double zoom; + if ([json valueForKey:@"zoom"] && [json valueForKey:@"zoom"] != [NSNull null]) { + zoom = [[json valueForKey:@"zoom"] doubleValue]; + } else { + zoom = self.mapCtrl.map.camera.zoom; + } + + double cameraPadding = 20; + if ([json valueForKey:@"padding"] && [json valueForKey:@"zoom"] != [NSNull null]) { + cameraPadding = [[json valueForKey:@"padding"] doubleValue]; + } + + + NSDictionary *latLng = nil; + double latitude; + double longitude; + GMSCameraPosition *cameraPosition; + GMSCoordinateBounds *cameraBounds = nil; + CGFloat scale = self.mapCtrl.screenScale; + + UIEdgeInsets paddingUiEdgeInsets = UIEdgeInsetsMake(cameraPadding / scale, cameraPadding / scale, cameraPadding / scale, cameraPadding / scale); + + if ([json objectForKey:@"target"]) { + NSString *targetClsName = [[json objectForKey:@"target"] className]; + if ([targetClsName isEqualToString:@"__NSCFArray"] || [targetClsName isEqualToString:@"__NSArrayM"] ) { + //-------------------------------------------- + // cameraPosition.target = [ + // new plugin.google.maps.LatLng(), + // ... + // new plugin.google.maps.LatLng() + // ] + //--------------------------------------------- + int i = 0; + NSArray *latLngList = [json objectForKey:@"target"]; + GMSMutablePath *path = [GMSMutablePath path]; + for (i = 0; i < [latLngList count]; i++) { + latLng = [latLngList objectAtIndex:i]; + latitude = [[latLng valueForKey:@"lat"] doubleValue]; + longitude = [[latLng valueForKey:@"lng"] doubleValue]; + [path addLatitude:latitude longitude:longitude]; + } + + cameraBounds = [[GMSCoordinateBounds alloc] initWithPath:path]; + //CLLocationCoordinate2D center = cameraBounds.center; + + cameraPosition = [self.mapCtrl.map cameraForBounds:cameraBounds insets:paddingUiEdgeInsets]; + } else { + //------------------------------------------------------------------ + // cameraPosition.target = new plugin.google.maps.LatLng(); + //------------------------------------------------------------------ + + latLng = [json objectForKey:@"target"]; + latitude = [[latLng valueForKey:@"lat"] doubleValue]; + longitude = [[latLng valueForKey:@"lng"] doubleValue]; + + cameraPosition = [GMSCameraPosition cameraWithLatitude:latitude + longitude:longitude + zoom:zoom + bearing:bearing + viewingAngle:angle]; + } + } else { + cameraPosition = [GMSCameraPosition cameraWithLatitude:self.mapCtrl.map.camera.target.latitude + longitude:self.mapCtrl.map.camera.target.longitude + zoom:zoom + bearing:bearing + viewingAngle:angle]; + } + + float duration = 5.0f; + if ([json objectForKey:@"duration"]) { + duration = [[json objectForKey:@"duration"] floatValue] / 1000; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + + if ([action isEqual: @"animateCamera"]) { + [CATransaction begin]; { + [CATransaction setAnimationDuration: duration]; + + //[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]]; + [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; + + [CATransaction setCompletionBlock:^{ + if (self.isRemoved) { + return; + } + if (cameraBounds != nil){ + + GMSCameraPosition *cameraPosition2 = [GMSCameraPosition cameraWithLatitude:self.mapCtrl.map.camera.target.latitude + longitude:self.mapCtrl.map.camera.target.longitude + zoom:self.mapCtrl.map.camera.zoom + bearing:bearing + viewingAngle:angle]; + + [self.mapCtrl.map setCamera:cameraPosition2]; + + } else { + if (bearing == 0) { + GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:self.mapCtrl.map.projection.visibleRegion]; + [self.mapCtrl.map cameraForBounds:bounds insets:paddingUiEdgeInsets]; + } + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + [self.mapCtrl.map animateToCameraPosition: cameraPosition]; + }[CATransaction commit]; + } + if ([action isEqual: @"moveCamera"]) { + [self.mapCtrl.map setCamera:cameraPosition]; + + if (cameraBounds != nil){ + double delayInSeconds = 0.5; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + GMSCameraPosition *cameraPosition2 = [GMSCameraPosition cameraWithLatitude:self.mapCtrl.map.camera.target.latitude + longitude:self.mapCtrl.map.camera.target.longitude + zoom:self.mapCtrl.map.camera.zoom + bearing:bearing + viewingAngle:angle]; + + if (self.isRemoved) { + [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR] callbackId:command.callbackId]; + return; + } + [self.mapCtrl.map setCamera:cameraPosition2]; + dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:self.mapCtrl.map.projection.visibleRegion]; + [self.mapCtrl.map cameraForBounds:bounds insets:paddingUiEdgeInsets]; + [self.mapCtrl.view setHidden:NO]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + + }); + + }); + + } else { + [self.mapCtrl.view setHidden:NO]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + + } + }]; + +} +-(void)updateCameraPosition: (NSString*)action command:(CDVInvokedUrlCommand *)command { + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSDictionary *json = [command.arguments objectAtIndex:0]; + [self _changeCameraPosition:action requestMethod:@"updateCameraPosition" params:json command:command]; + }]; +} + +- (void)setActiveMarkerId:(CDVInvokedUrlCommand*)command { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + if (marker != nil) { + self.mapCtrl.map.selectedMarker = marker; + self.mapCtrl.activeMarker = marker; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + + +- (void)toDataURL:(CDVInvokedUrlCommand *)command { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSDictionary *opts = [command.arguments objectAtIndex:0]; + BOOL uncompress = NO; + if ([opts objectForKey:@"uncompress"]) { + uncompress = [[opts objectForKey:@"uncompress"] boolValue]; + } + + if (uncompress) { + UIGraphicsBeginImageContextWithOptions(self.mapCtrl.view.frame.size, NO, 0.0f); + } else { + UIGraphicsBeginImageContext(self.mapCtrl.view.frame.size); + } + [self.mapCtrl.view drawViewHierarchyInRect:self.mapCtrl.map.layer.bounds afterScreenUpdates:NO]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSData *imageData = UIImagePNGRepresentation(image); + NSString* base64Encoded = [imageData base64EncodedStringWithOptions:0]; + NSString* base64EncodedWithData = [@"data:image/png;base64," stringByAppendingString:base64Encoded]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:base64EncodedWithData]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Maps an Earth coordinate to a point coordinate in the map's view. + */ +- (void)fromLatLngToPoint:(CDVInvokedUrlCommand*)command { + + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + float latitude = [[command.arguments objectAtIndex:0] floatValue]; + float longitude = [[command.arguments objectAtIndex:1] floatValue]; + CGPoint point = [self.mapCtrl.map.projection + pointForCoordinate:CLLocationCoordinate2DMake(latitude, longitude)]; + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSMutableArray *pointJSON = [[NSMutableArray alloc] init]; + [pointJSON addObject:[NSNumber numberWithDouble:point.x]]; + [pointJSON addObject:[NSNumber numberWithDouble:point.y]]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:pointJSON]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Maps a point coordinate in the map's view to an Earth coordinate. + */ +- (void)fromPointToLatLng:(CDVInvokedUrlCommand*)command { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + float pointX = [[command.arguments objectAtIndex:0] floatValue]; + float pointY = [[command.arguments objectAtIndex:1] floatValue]; + CLLocationCoordinate2D latLng = [self.mapCtrl.map.projection + coordinateForPoint:CGPointMake(pointX, pointY)]; + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSMutableArray *latLngJSON = [[NSMutableArray alloc] init]; + [latLngJSON addObject:[NSNumber numberWithDouble:latLng.latitude]]; + [latLngJSON addObject:[NSNumber numberWithDouble:latLng.longitude]]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:latLngJSON]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +- (void)_setOptions:(NSDictionary *)initOptions requestMethod:(NSString *)requestMethod command:(CDVInvokedUrlCommand *)command { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + BOOL isEnabled = NO; + //controls + NSDictionary *controls = [initOptions objectForKey:@"controls"]; + if (controls) { + //compass + if ([controls valueForKey:@"compass"] != nil) { + isEnabled = [[controls valueForKey:@"compass"] boolValue]; + if (isEnabled == true) { + self.mapCtrl.map.settings.compassButton = YES; + } else { + self.mapCtrl.map.settings.compassButton = NO; + } + } + //myLocationButton + if ([controls valueForKey:@"myLocationButton"] != nil) { + isEnabled = [[controls valueForKey:@"myLocationButton"] boolValue]; + if (isEnabled == true) { + self.mapCtrl.map.settings.myLocationButton = YES; + } else { + self.mapCtrl.map.settings.myLocationButton = NO; + } + } + //myLocation + if ([controls valueForKey:@"myLocation"] != nil) { + isEnabled = [[controls valueForKey:@"myLocation"] boolValue]; + if (isEnabled == true) { + self.mapCtrl.map.myLocationEnabled = YES; + } else { + self.mapCtrl.map.myLocationEnabled = NO; + } + } + //indoorPicker + if ([controls valueForKey:@"indoorPicker"] != nil) { + isEnabled = [[controls valueForKey:@"indoorPicker"] boolValue]; + if (isEnabled == true) { + self.mapCtrl.map.settings.indoorPicker = YES; + } else { + self.mapCtrl.map.settings.indoorPicker = NO; + } + } + } else { + self.mapCtrl.map.settings.compassButton = YES; + } + + //gestures + NSDictionary *gestures = [initOptions objectForKey:@"gestures"]; + if (gestures) { + //rotate + if ([gestures valueForKey:@"rotate"] != nil) { + isEnabled = [[gestures valueForKey:@"rotate"] boolValue]; + self.mapCtrl.map.settings.rotateGestures = isEnabled; + } + //scroll + if ([gestures valueForKey:@"scroll"] != nil) { + isEnabled = [[gestures valueForKey:@"scroll"] boolValue]; + self.mapCtrl.map.settings.scrollGestures = isEnabled; + } + //tilt + if ([gestures valueForKey:@"tilt"] != nil) { + isEnabled = [[gestures valueForKey:@"tilt"] boolValue]; + self.mapCtrl.map.settings.tiltGestures = isEnabled; + } + //zoom + if ([gestures valueForKey:@"zoom"] != nil) { + isEnabled = [[gestures valueForKey:@"zoom"] boolValue]; + self.mapCtrl.map.settings.zoomGestures = isEnabled; + } + } + //preferences + NSDictionary *preferences = [initOptions objectForKey:@"preferences"]; + if (preferences) { + //padding + if ([preferences valueForKey:@"padding"] != nil) { + NSDictionary *padding = [preferences valueForKey:@"padding"]; + UIEdgeInsets current = self.mapCtrl.map.padding; + if ([padding objectForKey:@"left"] != nil) { + current.left = [[padding objectForKey:@"left"] floatValue]; + } + if ([padding objectForKey:@"top"] != nil) { + current.top = [[padding objectForKey:@"top"] floatValue]; + } + if ([padding objectForKey:@"bottom"] != nil) { + current.bottom = [[padding objectForKey:@"bottom"] floatValue]; + } + if ([padding objectForKey:@"right"] != nil) { + current.right = [[padding objectForKey:@"right"] floatValue]; + } + + UIEdgeInsets newPadding = UIEdgeInsetsMake(current.top, current.left, current.bottom, current.right); + [self.mapCtrl.map setPadding:newPadding]; + } + //zoom + if ([preferences valueForKey:@"zoom"] != nil) { + NSDictionary *zoom = [preferences valueForKey:@"zoom"]; + float minZoom = self.mapCtrl.map.minZoom; + float maxZoom = self.mapCtrl.map.maxZoom; + if ([zoom objectForKey:@"minZoom"] != nil) { + minZoom = [[zoom objectForKey:@"minZoom"] doubleValue]; + } + if ([zoom objectForKey:@"maxZoom"] != nil) { + maxZoom = [[zoom objectForKey:@"maxZoom"] doubleValue]; + } + + [self.mapCtrl.map setMinZoom:minZoom maxZoom:maxZoom]; + } + + // gestureBounds + if ([preferences valueForKey:@"gestureBounds"] != nil) { + NSDictionary *latLng = nil; + double latitude, longitude; + int i = 0; + NSArray *latLngList = [preferences objectForKey:@"gestureBounds"]; + GMSMutablePath *path = [GMSMutablePath path]; + for (i = 0; i < [latLngList count]; i++) { + latLng = [latLngList objectAtIndex:i]; + latitude = [[latLng valueForKey:@"lat"] doubleValue]; + longitude = [[latLng valueForKey:@"lng"] doubleValue]; + [path addLatitude:latitude longitude:longitude]; + } + + [self.mapCtrl.map setCameraTargetBounds:[[GMSCoordinateBounds alloc] initWithPath:path]]; + } + } + + //styles + NSString *styles = [initOptions valueForKey:@"styles"]; + if (styles) { + NSError *error; + GMSMapStyle *mapStyle = [GMSMapStyle styleWithJSONString:styles error:&error]; + if (mapStyle != nil) { + self.mapCtrl.map.mapStyle = mapStyle; + self.mapCtrl.map.mapType = kGMSTypeNormal; + } else { + NSLog(@"Your specified map style is incorrect : %@", error.description); + } + } else { + + //mapType + NSString *typeStr = [initOptions valueForKey:@"mapType"]; + if (typeStr) { + + NSDictionary *mapTypes = [NSDictionary dictionaryWithObjectsAndKeys: + ^() {return kGMSTypeHybrid; }, @"MAP_TYPE_HYBRID", + ^() {return kGMSTypeSatellite; }, @"MAP_TYPE_SATELLITE", + ^() {return kGMSTypeTerrain; }, @"MAP_TYPE_TERRAIN", + ^() {return kGMSTypeNormal; }, @"MAP_TYPE_NORMAL", + ^() {return kGMSTypeNone; }, @"MAP_TYPE_NONE", + nil]; + + typedef GMSMapViewType (^CaseBlock)(); + GMSMapViewType mapType; + CaseBlock caseBlock = mapTypes[typeStr]; + if (caseBlock) { + // Change the map type + mapType = caseBlock(); + self.mapCtrl.map.mapType = mapType; + } + } + } + + // Redraw the map mandatory + [self.mapCtrl.map setNeedsDisplay]; + + if ([initOptions valueForKey:@"camera"] && [initOptions valueForKey:@"camera"] != [NSNull null]) { + //------------------------------------------ + // Case : The camera option is specified. + //------------------------------------------ + NSDictionary *cameraOpts = [initOptions objectForKey:@"camera"]; + [self _changeCameraPosition:@"moveCamera" requestMethod:requestMethod params:cameraOpts command:command]; + } else { + [self.mapCtrl mapView:self.mapCtrl.map didChangeCameraPosition:self.mapCtrl.map.camera]; + [self.mapCtrl.view setHidden:NO]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + }]; + +}; + +- (void)setOptions:(CDVInvokedUrlCommand *)command { + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSDictionary *initOptions = [command.arguments objectAtIndex:0]; + [initOptions setValue:@"setOptions" forKeyPath:@"method"]; + [self _setOptions:initOptions requestMethod:@"setOptions" command:command]; + }]; +} + + +- (void)setPadding:(CDVInvokedUrlCommand *)command { + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSDictionary *paddingJson = [command.arguments objectAtIndex:0]; + float top = [[paddingJson objectForKey:@"top"] floatValue]; + float left = [[paddingJson objectForKey:@"left"] floatValue]; + float right = [[paddingJson objectForKey:@"right"] floatValue]; + float bottom = [[paddingJson objectForKey:@"bottom"] floatValue]; + + UIEdgeInsets padding = UIEdgeInsetsMake(top, left, bottom, right); + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [self.mapCtrl.map setPadding:padding]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +- (void)getFocusedBuilding:(CDVInvokedUrlCommand*)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + GMSIndoorBuilding *building = self.mapCtrl.map.indoorDisplay.activeBuilding; + if (building != nil || [building.levels count] == 0) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + GMSIndoorLevel *activeLevel = self.mapCtrl.map.indoorDisplay.activeLevel; + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + NSUInteger activeLevelIndex = [building.levels indexOfObject:activeLevel]; + [result setObject:[NSNumber numberWithInteger:activeLevelIndex] forKey:@"activeLevelIndex"]; + [result setObject:[NSNumber numberWithInteger:building.defaultLevelIndex] forKey:@"defaultLevelIndex"]; + + GMSIndoorLevel *level; + NSMutableDictionary *levelInfo; + NSMutableArray *levels = [NSMutableArray array]; + for (level in building.levels) { + levelInfo = [NSMutableDictionary dictionary]; + + [levelInfo setObject:[NSString stringWithString:level.name] forKey:@"name"]; + [levelInfo setObject:[NSString stringWithString:level.shortName] forKey:@"shortName"]; + [levels addObject:levelInfo]; + } + [result setObject:levels forKey:@"levels"]; + + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + + +- (void)stopAnimation:(CDVInvokedUrlCommand*)command { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [self.mapCtrl.map.layer removeAllAnimations]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + +} +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMapViewController.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMapViewController.h new file mode 100644 index 0000000..33aa634 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMapViewController.h @@ -0,0 +1,26 @@ +// +// PluginMapViewController.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import +#import +#import +#import "PluginUtil.h" +#import "IPluginProtocol.h" +#import "PluginObjects.h" +#import "PluginViewController.h" +#import + +@interface PluginMapViewController : PluginViewController + +@property (nonatomic) GMSMarker* activeMarker; +@property (nonatomic) GMSMapView* map; + +- (BOOL)didTapMyLocationButtonForMapView:(GMSMapView *)mapView; +- (void)didChangeActiveBuilding: (GMSIndoorBuilding *)building; +- (void)didChangeActiveLevel: (GMSIndoorLevel *)level; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMapViewController.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMapViewController.m new file mode 100644 index 0000000..4cf5352 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMapViewController.m @@ -0,0 +1,1066 @@ +// +// PluginMapViewController.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginMapViewController.h" + +@implementation PluginMapViewController + +- (void)mapView:(GMSMapView *)mapView didTapPOIWithPlaceID:(NSString *)placeID name:(NSString *)name location:(CLLocationCoordinate2D)location { + NSString* jsName = [name stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]; + jsName = [jsName stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; + jsName = [jsName stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; + + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onMapEvent', args: ['%@', \"%@\", new plugin.google.maps.LatLng(%f,%f)]});}", + self.overlayId, self.overlayId, @"poi_click", placeID, jsName, location.latitude, location.longitude]; + [self execJS:jsString]; +} + +/** + * Called when the My Location button is tapped. + * + * @return YES if the listener has consumed the event (i.e., the default behavior should not occur), + * NO otherwise (i.e., the default behavior should occur). The default behavior is for the + * camera to move such that it is centered on the user location. + */ +- (BOOL)didTapMyLocationButtonForMapView:(GMSMapView *)mapView { + + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onMapEvent', args: []});}", + self.overlayId, self.overlayId, @"my_location_button_click"]; + [self execJS:jsString]; + return NO; +} + +#pragma mark - GMSMapViewDelegate +- (void)mapView:(GMSMapView *)mapView didTapMyLocation:(CLLocationCoordinate2D)location { + + NSMutableDictionary *latLng = [NSMutableDictionary dictionary]; + [latLng setObject:[NSNumber numberWithDouble:location.latitude] forKey:@"lat"]; + [latLng setObject:[NSNumber numberWithDouble:location.longitude] forKey:@"lng"]; + + NSMutableDictionary *json = [NSMutableDictionary dictionary]; + + [json setObject:latLng forKey:@"latLng"]; + [json setObject:[NSNumber numberWithFloat:[self.map.myLocation speed]] forKey:@"speed"]; + [json setObject:[NSNumber numberWithFloat:[self.map.myLocation altitude]] forKey:@"altitude"]; + + //todo: calcurate the correct accuracy based on horizontalAccuracy and verticalAccuracy + [json setObject:[NSNumber numberWithFloat:[self.map.myLocation horizontalAccuracy]] forKey:@"accuracy"]; + [json setObject:[NSNumber numberWithDouble:[self.map.myLocation.timestamp timeIntervalSince1970]] forKey:@"time"]; + [json setObject:[NSNumber numberWithInteger:[self.map.myLocation hash]] forKey:@"hashCode"]; + + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; + NSString* sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onMapEvent', args: [%@]});}", + self.overlayId, self.overlayId, @"my_location_click", sourceArrayString]; + [self execJS:jsString]; +} + +/** + * @callback the my location button is clicked. + */ +- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { + + + if (self.activeMarker) { + /* + NSString *clusterId_markerId =[NSString stringWithFormat:@"%@", self.activeMarker.userData]; + NSArray *tmp = [clusterId_markerId componentsSeparatedByString:@"_"]; + NSString *className = [tmp objectAtIndex:0]; + if ([className isEqualToString:@"markercluster"]) { + if ([clusterId_markerId containsString:@"-marker_"]) { + [self triggerClusterEvent:@"info_close" marker:self.activeMarker]; + } + } else { + [self triggerMarkerEvent:@"info_close" marker:self.activeMarker]; + } + */ + + //self.map.selectedMarker = nil; + self.activeMarker = nil; + } + + //NSArray *pluginNames =[self.plugins allKeys]; + //NSString *pluginName; + NSString *key; + NSDictionary *properties; + //CDVPlugin *plugin; + GMSCoordinateBounds *bounds; + GMSPath *path; + NSArray *keys; + NSNumber *isVisible, *geodesic, *isClickable; + NSMutableArray *boundsHitList = [NSMutableArray array]; + //NSMutableArray *boundsPluginList = [NSMutableArray array]; + int i,j; + float zIndex, maxZIndex; + NSString* hitKey = nil; + + + keys = [self.objects allKeys]; + for (j = 0; j < [keys count]; j++) { + key = [keys objectAtIndex:j]; + if ([key containsString:@"-marker"] || + [key containsString:@"property"] == NO) { + continue; + } + + properties = [self.objects objectForKey:key]; + //NSLog(@"--> key = %@, properties = %@", key, properties); + + // Skip invisible polyline + isVisible = (NSNumber *)[properties objectForKey:@"isVisible"]; + if ([isVisible boolValue] == NO) { + //NSLog(@"--> key = %@, isVisible = NO", key); + continue; + } + + // Skip isClickable polyline + isClickable = (NSNumber *)[properties objectForKey:@"isClickable"]; + if ([isClickable boolValue] == NO) { + //NSLog(@"--> key = %@, isClickable = NO", key); + continue; + } + //NSLog(@"--> key = %@, isVisible = YES, isClickable = YES", key); + + // Skip if the click point is out of the polyline bounds. + bounds = (GMSCoordinateBounds *)[properties objectForKey:@"bounds"]; + if ([bounds containsCoordinate:coordinate]) { + [boundsHitList addObject:key]; + //[boundsPluginList addObject:plugin]; + } + + } + /* + for (i = 0; i < [pluginNames count]; i++) { + pluginName = [pluginNames objectAtIndex:i]; + + // Skip marker class + if ([pluginName hasSuffix:@"-marker"]) { + continue; + } + + // Get the plugin (marker, polyline, polygon, circle, groundOverlay) + plugin = [self.plugins objectForKey:pluginName]; + + + keys = [plugin.objects allKeys]; + for (j = 0; j < [keys count]; j++) { + key = [keys objectAtIndex:j]; + if ([key containsString:@"property"]) { + properties = [plugin.objects objectForKey:key]; + + // Skip invisible polyline + isVisible = (NSNumber *)[properties objectForKey:@"isVisible"]; + if ([isVisible boolValue] == NO) { + //NSLog(@"--> key = %@, isVisible = NO", key); + continue; + } + + // Skip isClickable polyline + isClickable = (NSNumber *)[properties objectForKey:@"isClickable"]; + if ([isClickable boolValue] == NO) { + //NSLog(@"--> key = %@, isClickable = NO", key); + continue; + } + //NSLog(@"--> key = %@, isVisible = YES, isClickable = YES", key); + + // Skip if the click point is out of the polyline bounds. + bounds = (GMSCoordinateBounds *)[properties objectForKey:@"bounds"]; + if ([bounds containsCoordinate:coordinate]) { + [boundsHitList addObject:key]; + [boundsPluginList addObject:plugin]; + } + } + } + } + */ + + + CLLocationCoordinate2D origin = [self.map.projection coordinateForPoint:CGPointMake(0, 0)]; + CLLocationCoordinate2D hitArea = [self.map.projection coordinateForPoint:CGPointMake(1, 1)]; + CLLocationDistance threshold = GMSGeometryDistance(origin, hitArea); + + + + // + maxZIndex = -1; + CLLocationCoordinate2D touchPoint; + for (i = 0; i < [boundsHitList count]; i++) { + key = [boundsHitList objectAtIndex:i]; + //plugin = [boundsPluginList objectAtIndex:i]; + properties = [self.objects objectForKey:key]; + + + zIndex = [[properties objectForKey:@"zIndex"] floatValue]; + //NSLog(@"--> zIndex = %f, maxZIndex = %f", zIndex, maxZIndex); + if (zIndex < maxZIndex) { + continue; + } + + if ([key hasPrefix:@"polyline_"]) { + geodesic = (NSNumber *)[properties objectForKey:@"geodesic"]; + path = (GMSPath *)[properties objectForKey:@"mutablePath"]; + if ([geodesic boolValue] == YES) { + touchPoint = [PluginUtil isPointOnTheGeodesicLine:path coordinate:coordinate threshold:threshold projection:self.map.projection]; + if (CLLocationCoordinate2DIsValid(touchPoint)) { + maxZIndex = zIndex; + hitKey = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + continue; + } + } else { + touchPoint = [PluginUtil isPointOnTheLine:path coordinate:coordinate projection:self.map.projection]; + if (CLLocationCoordinate2DIsValid(touchPoint)) { + maxZIndex = zIndex; + hitKey = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + continue; + } + } + } + + if ([key hasPrefix:@"polygon_"]) { + path = (GMSPath *)[properties objectForKey:@"mutablePath"]; + if ([PluginUtil isPolygonContains:path coordinate:coordinate projection:self.map.projection]) { + touchPoint = coordinate; + maxZIndex = zIndex; + hitKey = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + continue; + } + } + + + if ([key hasPrefix:@"circle_"]) { + key = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + GMSCircle *circle = (GMSCircle *)[self.objects objectForKey:key]; + if ([PluginUtil isCircleContains:circle coordinate:coordinate]) { + touchPoint = coordinate; + maxZIndex = zIndex; + hitKey = key; + continue; + } + } + + if ([key hasPrefix:@"groundoverlay_"]) { + key = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + GMSGroundOverlay *groundOverlay = (GMSGroundOverlay *)[self.objects objectForKey:key]; + if ([groundOverlay.bounds containsCoordinate:coordinate]) { + touchPoint = coordinate; + maxZIndex = zIndex; + hitKey = key; + continue; + } + } + + } + + if (hitKey != nil) { + NSArray *tmp = [hitKey componentsSeparatedByString:@"_"]; + NSString *eventName = [NSString stringWithFormat:@"%@_click", [tmp objectAtIndex:0]]; + [self triggerOverlayEvent:eventName overlayId:hitKey coordinate:touchPoint]; + } else { + [self triggerMapEvent:@"map_click" coordinate:coordinate]; + } + + +} +/** + * @callback map long_click + */ +- (void) mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { + [self triggerMapEvent:@"map_long_click" coordinate:coordinate]; +} + +/** + * @callback plugin.google.maps.event.CAMERA_MOVE_START + */ +- (void) mapView:(GMSMapView *)mapView willMove:(BOOL)gesture +{ + self.isDragging = gesture; + + if (self.isDragging) { + [self triggerMapEvent:@"map_drag_start"]; + } + [self triggerCameraEvent:@"camera_move_start" position:self.map.camera]; +} + + +/** + * @callback plugin.google.maps.event.CAMERA_MOVE + */ +- (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position { + + if (self.isDragging) { + [self triggerMapEvent:@"map_drag"]; + } + [self triggerCameraEvent:(@"camera_move") position:position]; +} + +/** + * @callback plugin.google.maps.event.CAMERA_MOVE_END + */ +- (void) mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position +{ + if (self.isDragging) { + [self triggerMapEvent:@"map_drag_end"]; + } + [self triggerCameraEvent:(@"camera_move_end") position:position]; + self.isDragging = NO; +} + + +/** + * @callback marker info_click + */ +- (void) mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker +{ + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + [self triggerClusterEvent:@"info_click" marker:marker]; + } else { + [self triggerMarkerEvent:@"info_click" marker:marker]; + } + [self syncInfoWndPosition]; +} +/** + * Called after a marker's info window has been long pressed. + */ +- (void)mapView:(GMSMapView *)mapView didLongPressInfoWindowOfMarker:(GMSMarker *)marker { + + [self syncInfoWndPosition]; + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + [self triggerClusterEvent:@"info_long_click" marker:marker]; + } else { + [self triggerMarkerEvent:@"info_long_click" marker:marker]; + } +} +/** + * @callback plugin.google.maps.event.MARKER_DRAG_START + */ +- (void) mapView:(GMSMapView *) mapView didBeginDraggingMarker:(GMSMarker *)marker +{ + [self syncInfoWndPosition]; + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + [self triggerClusterEvent:@"marker_drag_start" marker:marker]; + } else { + [self triggerMarkerEvent:@"marker_drag_start" marker:marker]; + } +} +/** + * @callback plugin.google.maps.event.MARKER_DRAG_END + */ +- (void) mapView:(GMSMapView *) mapView didEndDraggingMarker:(GMSMarker *)marker +{ + [self syncInfoWndPosition]; + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + [self triggerClusterEvent:@"marker_drag_end" marker:marker]; + } else { + [self triggerMarkerEvent:@"marker_drag_end" marker:marker]; + } +} +/** + * @callback plugin.google.maps.event.MARKER_DRAG + */ +- (void) mapView:(GMSMapView *) mapView didDragMarker:(GMSMarker *)marker +{ + [self syncInfoWndPosition]; + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + [self triggerClusterEvent:@"marker_drag" marker:marker]; + } else { + [self triggerMarkerEvent:@"marker_drag" marker:marker]; + } +} + +- (void) syncInfoWndPosition { + if (self.activeMarker == nil) { + //NSLog(@"-->no active marker"); + return; + } + CLLocationCoordinate2D position = self.activeMarker.position; + CGPoint point = [self.map.projection + pointForCoordinate:CLLocationCoordinate2DMake(position.latitude, position.longitude)]; + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: 'syncPosition', callback: '_onSyncInfoWndPosition', args: [{x: %f, y: %f}]});}", + self.overlayId, self.overlayId, point.x, point.y ]; + [self execJS:jsString]; +} + +/** + * @callback plugin.google.maps.event.MARKER_CLICK + */ +- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { + + NSString *clusterId_markerId = [NSString stringWithString:marker.userData]; + + if ([clusterId_markerId containsString:@"markercluster_"]) { + if ([clusterId_markerId containsString:@"-marker_"]) { + //NSLog(@"--->activeMarker = %@", marker.userData); + self.map.selectedMarker = marker; + self.activeMarker = marker; + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + [self triggerClusterEvent:@"marker_click" marker:marker]; + } else { + [self triggerMarkerEvent:@"marker_click" marker:marker]; + } + } else { + if (self.activeMarker != nil) { + NSString *markerTag = [NSString stringWithFormat:@"%@", self.activeMarker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + if ([markerTag containsString:@"-marker_"]) { + [self triggerClusterEvent:@"info_close" marker:self.activeMarker]; + } +// } else { +// [self triggerMarkerEvent:@"info_close" marker:self.activeMarker]; + } + } + [self triggerClusterEvent:@"cluster_click" marker:marker]; + } + } else { + [self execJS:@"javascript:if(window.cordova){cordova.fireDocumentEvent('plugin_touch', {});}"]; + [self triggerMarkerEvent:@"marker_click" marker:marker]; + //NSLog(@"--->activeMarker = %@", marker.userData); + self.map.selectedMarker = marker; + self.activeMarker = marker; + } + + //NSArray *tmp = [clusterId_markerId componentsSeparatedByString:@"_"]; + //NSString *className = [tmp objectAtIndex:0]; + + // Get the marker plugin + //NSString *pluginId = [NSString stringWithFormat:@"%@-%@", self.overlayId, className]; + //CDVPlugin *plugin = [self.plugins objectForKey:pluginId]; + + // Get the marker properties + NSString *markerPropertyId = [NSString stringWithFormat:@"marker_property_%@", clusterId_markerId]; + NSDictionary *properties = [self.objects objectForKey:markerPropertyId]; + + BOOL disableAutoPan = false; + if ([properties objectForKey:@"disableAutoPan"] != nil) { + disableAutoPan = [[properties objectForKey:@"disableAutoPan"] boolValue]; + if (disableAutoPan) { + return YES; + } + } + + //-------------------------- + // Pan the camera mondatory + //-------------------------- + GMSCameraPosition* cameraPosition = [GMSCameraPosition + cameraWithTarget:marker.position zoom:self.map.camera.zoom]; + + [self.map animateToCameraPosition:cameraPosition]; + return YES; +} + +- (void)mapView:(GMSMapView *)mapView didCloseInfoWindowOfMarker:(nonnull GMSMarker *)marker { + + // Get the marker plugin + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + if ([markerTag containsString:@"-marker_"]) { + [self triggerClusterEvent:@"info_close" marker:marker]; + } + } else { + [self triggerMarkerEvent:@"info_close" marker:marker]; + } + + //self.map.selectedMarker = nil; // <-- this causes the didCloseInfoWindowOfMarker event again + //self.activeMarker = nil; // <-- This causes HTMLinfoWindow is not able to close when you tap on the map. +} + + +/* + - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { + NSString *overlayClass = NSStringFromClass([overlay class]); + if ([overlayClass isEqualToString:@"GMSPolygon"] || + [overlayClass isEqualToString:@"GMSPolyline"] || + [overlayClass isEqualToString:@"GMSCircle"] || + [overlayClass isEqualToString:@"GMSGroundOverlay"]) { + [self triggerOverlayEvent:@"overlay_click" id:overlay.title]; + } + } + */ + +/** + * Map tiles are loaded + */ +- (void) mapViewDidFinishTileRendering:(GMSMapView *)mapView { + // no longer available from v2.2.0 + //[self triggerMapEvent:@"map_loaded"]; +} + +/** + * plugin.google.maps.event.MAP_***()) events + */ +- (void)triggerMapEvent: (NSString *)eventName +{ + + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onMapEvent', args: []});}", + self.overlayId, self.overlayId, eventName]; + [self execJS:jsString]; +} + +/** + * plugin.google.maps.event.MAP_***(new google.maps.LatLng(lat,lng)) events + */ +- (void)triggerMapEvent: (NSString *)eventName coordinate:(CLLocationCoordinate2D)coordinate +{ + + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onMapEvent', args: [new plugin.google.maps.LatLng(%f,%f)]});}", + self.overlayId, self.overlayId, eventName, coordinate.latitude, coordinate.longitude]; + [self execJS:jsString]; +} + +/** + * plugin.google.maps.event.CAMERA_*** events + */ +- (void)triggerCameraEvent: (NSString *)eventName position:(GMSCameraPosition *)position +{ + + NSMutableDictionary *target = [NSMutableDictionary dictionary]; + [target setObject:[NSNumber numberWithDouble:position.target.latitude] forKey:@"lat"]; + [target setObject:[NSNumber numberWithDouble:position.target.longitude] forKey:@"lng"]; + + NSMutableDictionary *json = [NSMutableDictionary dictionary]; + [json setObject:[NSNumber numberWithFloat:position.bearing] forKey:@"bearing"]; + [json setObject:target forKey:@"target"]; + [json setObject:[NSNumber numberWithDouble:position.viewingAngle] forKey:@"tilt"]; + [json setObject:[NSNumber numberWithInt:(int)position.hash] forKey:@"hashCode"]; + [json setObject:[NSNumber numberWithFloat:position.zoom] forKey:@"zoom"]; + + + GMSVisibleRegion visibleRegion = self.map.projection.visibleRegion; + GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:visibleRegion]; + NSMutableDictionary *northeast = [NSMutableDictionary dictionary]; + NSMutableDictionary *southwest = [NSMutableDictionary dictionary]; + + [northeast setObject:[NSNumber numberWithDouble:bounds.northEast.latitude] forKey:@"lat"]; + [northeast setObject:[NSNumber numberWithDouble:bounds.northEast.longitude] forKey:@"lng"]; + [json setObject:northeast forKey:@"northeast"]; + [southwest setObject:[NSNumber numberWithDouble:bounds.southWest.latitude] forKey:@"lat"]; + [southwest setObject:[NSNumber numberWithDouble:bounds.southWest.longitude] forKey:@"lng"]; + [json setObject:southwest forKey:@"southwest"]; + + + + NSMutableDictionary *farLeft = [NSMutableDictionary dictionary]; + [farLeft setObject:[NSNumber numberWithDouble:visibleRegion.farLeft.latitude] forKey:@"lat"]; + [farLeft setObject:[NSNumber numberWithDouble:visibleRegion.farLeft.longitude] forKey:@"lng"]; + [json setObject:farLeft forKey:@"farLeft"]; + + NSMutableDictionary *farRight = [NSMutableDictionary dictionary]; + [farRight setObject:[NSNumber numberWithDouble:visibleRegion.farRight.latitude] forKey:@"lat"]; + [farRight setObject:[NSNumber numberWithDouble:visibleRegion.farRight.longitude] forKey:@"lng"]; + [json setObject:farRight forKey:@"farRight"]; + + NSMutableDictionary *nearLeft = [NSMutableDictionary dictionary]; + [nearLeft setObject:[NSNumber numberWithDouble:visibleRegion.nearLeft.latitude] forKey:@"lat"]; + [nearLeft setObject:[NSNumber numberWithDouble:visibleRegion.nearLeft.longitude] forKey:@"lng"]; + [json setObject:nearLeft forKey:@"nearLeft"]; + + NSMutableDictionary *nearRight = [NSMutableDictionary dictionary]; + [nearRight setObject:[NSNumber numberWithDouble:visibleRegion.nearRight.latitude] forKey:@"lat"]; + [nearRight setObject:[NSNumber numberWithDouble:visibleRegion.nearRight.longitude] forKey:@"lng"]; + [json setObject:nearRight forKey:@"nearRight"]; + + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; + NSString* sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onCameraEvent', args: [%@]});}", + self.overlayId, self.overlayId, eventName, sourceArrayString]; + [self execJS:jsString]; + + [self syncInfoWndPosition]; +} + + +/** + * cluster_*** events + */ +- (void)triggerClusterEvent: (NSString *)eventName marker:(GMSMarker *)marker +{ + if (marker.userData == nil) { + return; + } + + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + NSArray *tmp = [markerTag componentsSeparatedByString:@"-"]; + NSString *clusterId = [tmp objectAtIndex:0]; + NSString *markerId = [tmp objectAtIndex:1]; + + // Get the marker plugin + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onClusterEvent', args: ['%@', '%@', new plugin.google.maps.LatLng(%f, %f)]});}", + self.overlayId, self.overlayId, eventName, clusterId, markerId, + marker.position.latitude, + marker.position.longitude]; + [self execJS:jsString]; +} + +/** + * plugin.google.maps.event.MARKER_*** events + */ +- (void)triggerMarkerEvent: (NSString *)eventName marker:(GMSMarker *)marker +{ + + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + NSArray *tmp = [markerTag componentsSeparatedByString:@"-"]; + NSString *markerId = [tmp objectAtIndex:([tmp count] - 1)]; + + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onMarkerEvent', args: ['%@', new plugin.google.maps.LatLng(%f, %f)]});}", + self.overlayId, self.overlayId, eventName, markerId, + marker.position.latitude, + marker.position.longitude]; + [self execJS:jsString]; +} + +/** + * Involve App._onOverlayEvent + */ +- (void)triggerOverlayEvent: (NSString *)eventName overlayId:(NSString*)overlayId coordinate:(CLLocationCoordinate2D)coordinate +{ + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onOverlayEvent', args: ['%@', new plugin.google.maps.LatLng(%f, %f)]});}", + self.overlayId, self.overlayId, eventName, overlayId, coordinate.latitude, coordinate.longitude]; + [self execJS:jsString]; +} + +//future support: custom info window +-(UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker*)marker +{ + CGSize rectSize; + CGSize textSize; + CGSize snippetSize; + UIFont *titleFont; + UIFont *snippetFont; + UIImage *base64Image; + + Boolean isTextMode = false; + NSString *title = marker.title; + NSString *snippet = marker.snippet; + + + // Get the marker plugin + //NSString *pluginId = [NSString stringWithFormat:@"%@-marker", self.overlayId]; + //CDVPlugin *plugin = [self.plugins objectForKey:pluginId]; + + // Get the marker properties + NSString *markerPropertyId = [NSString stringWithFormat:@"marker_property_%@", marker.userData]; + NSDictionary *properties = [self.objects objectForKey:markerPropertyId]; + Boolean useHtmlInfoWnd = marker.title == nil && marker.snippet == nil; + + if (useHtmlInfoWnd) { + [self syncInfoWndPosition]; + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + [self triggerClusterEvent:@"info_open" marker:marker]; + } else { + [self triggerMarkerEvent:@"info_open" marker:marker]; + } + + return [[UIView alloc]initWithFrame:CGRectMake(0, 0, 1, 1)]; + } + + if (title == nil) { + return NULL; + } + + NSString *markerTag = [NSString stringWithFormat:@"%@", marker.userData]; + if ([markerTag hasPrefix:@"markercluster_"]) { + [self triggerClusterEvent:@"info_open" marker:marker]; + } else { + [self triggerMarkerEvent:@"info_open" marker:marker]; + } + + // Load styles + NSDictionary *styles = nil; + if ([properties objectForKey:@"styles"]) { + styles = [properties objectForKey:@"styles"]; + } + + // Load images + UIImage *leftImg = nil; + UIImage *rightImg = nil;[self loadImageFromGoogleMap:@"bubble_right@2x"]; + leftImg = [self loadImageFromGoogleMap:@"bubble_left@2x"]; + rightImg = [self loadImageFromGoogleMap:@"bubble_right@2x"]; + float scale = leftImg.scale; + int sizeEdgeWidth = 10; + + int width = 0; + + if (styles && [styles objectForKey:@"width"]) { + NSString *widthString = [styles valueForKey:@"width"]; + + // check if string is numeric + NSNumberFormatter *nf = [[NSNumberFormatter alloc] init]; + BOOL isNumeric = [nf numberFromString:widthString] != nil; + + if ([widthString hasSuffix:@"%"]) { + double widthDouble = [[widthString stringByReplacingOccurrencesOfString:@"%" withString:@""] doubleValue]; + + width = (int)((double)mapView.frame.size.width * (widthDouble / 100)); + } else if (isNumeric) { + double widthDouble = [widthString doubleValue]; + + if (widthDouble <= 1.0) { + width = (int)((double)mapView.frame.size.width * (widthDouble)); + } else { + width = (int)widthDouble; + } + } + } + + int maxWidth = 0; + + if (styles && [styles objectForKey:@"maxWidth"]) { + NSString *widthString = [styles valueForKey:@"maxWidth"]; + + NSNumberFormatter *nf = [[NSNumberFormatter alloc] init]; + BOOL isNumeric = [nf numberFromString:widthString] != nil; + + if ([widthString hasSuffix:@"%"]) { + double widthDouble = [[widthString stringByReplacingOccurrencesOfString:@"%" withString:@""] doubleValue]; + + maxWidth = (int)((double)mapView.frame.size.width * (widthDouble / 100)); + + // make sure to take padding into account. + maxWidth -= sizeEdgeWidth; + } else if (isNumeric) { + double widthDouble = [widthString doubleValue]; + + if (widthDouble <= 1.0) { + maxWidth = (int)((double)mapView.frame.size.width * (widthDouble)); + } else { + maxWidth = (int)widthDouble; + } + } + } + + //------------------------------------- + // Calculate the size for the contents + //------------------------------------- + if ([title rangeOfString:@"data:image/"].location != NSNotFound && + [title rangeOfString:@";base64,"].location != NSNotFound) { + + isTextMode = false; + NSArray *tmp = [title componentsSeparatedByString:@","]; + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:[tmp objectAtIndex:1] options:0]; + base64Image = [[UIImage alloc] initWithData:decodedData]; + rectSize = CGSizeMake(base64Image.size.width + leftImg.size.width, base64Image.size.height + leftImg.size.height / 2); + + } else { + + isTextMode = true; + + BOOL isBold = FALSE; + BOOL isItalic = FALSE; + if (styles) { + if ([[styles objectForKey:@"font-style"] isEqualToString:@"italic"]) { + isItalic = TRUE; + } + if ([[styles objectForKey:@"font-weight"] isEqualToString:@"bold"]) { + isBold = TRUE; + } + } + if (isBold == TRUE && isItalic == TRUE) { + // ref: http://stackoverflow.com/questions/4713236/how-do-i-set-bold-and-italic-on-uilabel-of-iphone-ipad#21777132 + titleFont = [UIFont systemFontOfSize:17.0f]; + UIFontDescriptor *fontDescriptor = [titleFont.fontDescriptor + fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic]; + titleFont = [UIFont fontWithDescriptor:fontDescriptor size:0]; + } else if (isBold == TRUE && isItalic == FALSE) { + titleFont = [UIFont boldSystemFontOfSize:17.0f]; + } else if (isBold == TRUE && isItalic == FALSE) { + titleFont = [UIFont italicSystemFontOfSize:17.0f]; + } else { + titleFont = [UIFont systemFontOfSize:17.0f]; + } + + // Calculate the size for the title strings + CGRect textRect = [title boundingRectWithSize:CGSizeMake(mapView.frame.size.width - 13, mapView.frame.size.height - 13) + options:NSStringDrawingUsesLineFragmentOrigin + attributes:@{NSFontAttributeName:titleFont} + context:nil]; + textSize = textRect.size; + rectSize = CGSizeMake(textSize.width + 10, textSize.height + 22); + + // Calculate the size for the snippet strings + if (snippet) { + snippetFont = [UIFont systemFontOfSize:12.0f]; + snippet = [snippet stringByReplacingOccurrencesOfString:@"\n" withString:@""]; + textRect = [snippet boundingRectWithSize:CGSizeMake(mapView.frame.size.width - 13, mapView.frame.size.height - 13) + options:NSStringDrawingUsesLineFragmentOrigin + attributes:@{NSFontAttributeName:snippetFont} + context:nil]; + snippetSize = textRect.size; + rectSize.height += snippetSize.height + 4; + if (rectSize.width < snippetSize.width + leftImg.size.width) { + rectSize.width = snippetSize.width + leftImg.size.width; + } + } + } + if (rectSize.width < leftImg.size.width * scale) { + rectSize.width = leftImg.size.width * scale; + } else { + rectSize.width += sizeEdgeWidth; + } + + if (width > 0) { + rectSize.width = width; + } + if (maxWidth > 0 && + maxWidth < rectSize.width) { + rectSize.width = maxWidth; + } + + //------------------------------------- + // Draw the the info window + //------------------------------------- + UIGraphicsBeginImageContextWithOptions(rectSize, NO, 0.0f); + + CGRect trimArea = CGRectMake(15, 0, 5, MIN(45, rectSize.height - 20)); + + trimArea = CGRectMake(15, 0, 15, leftImg.size.height); + if (scale > 1.0f) { + trimArea = CGRectMake(trimArea.origin.x * scale, + trimArea.origin.y * scale, + trimArea.size.width * scale +1, + trimArea.size.height * scale); + } + CGImageRef shadowImageRef = CGImageCreateWithImageInRect(leftImg.CGImage, trimArea); + UIImage *shadowImageLeft = [UIImage imageWithCGImage:shadowImageRef scale:scale orientation:UIImageOrientationUp]; + UIImage *shadowImageRight = [UIImage imageWithCGImage:shadowImageRef scale:scale orientation:UIImageOrientationUpMirrored]; + CGImageRelease(shadowImageRef); + + int y; + int i = 0; + int x = shadowImageLeft.size.width; + float centerPos = rectSize.width * 0.5f; + while (centerPos - x > shadowImageLeft.size.width) { + y = 1; + while (y + shadowImageLeft.size.height < rectSize.height) { + [shadowImageLeft drawAtPoint:CGPointMake(centerPos - x, y)]; + [shadowImageRight drawAtPoint:CGPointMake(centerPos + x - shadowImageLeft.size.width, y)]; + y += shadowImageRight.size.height; + } + y = rectSize.height - shadowImageLeft.size.height; + [shadowImageLeft drawAtPoint:CGPointMake(centerPos - x, y)]; + [shadowImageRight drawAtPoint:CGPointMake(centerPos + x - shadowImageLeft.size.width, y)]; + + if (i == 0) { + x += 5; + + trimArea = CGRectMake(15, 0, 5, leftImg.size.height); + if (scale > 1.0f) { + trimArea = CGRectMake(trimArea.origin.x * scale, + trimArea.origin.y * scale, + trimArea.size.width * scale, + trimArea.size.height * scale); + } + shadowImageRef = CGImageCreateWithImageInRect(leftImg.CGImage, trimArea); + shadowImageLeft = [UIImage imageWithCGImage:shadowImageRef scale:scale orientation:UIImageOrientationUp]; + shadowImageRight = [UIImage imageWithCGImage:shadowImageRef scale:scale orientation:UIImageOrientationUpMirrored]; + CGImageRelease(shadowImageRef); + + } else { + x += shadowImageLeft.size.width; + } + i++; + } + + // Draw left & right side edges + x -= shadowImageLeft.size.width; + trimArea = CGRectMake(0, 0, sizeEdgeWidth, leftImg.size.height); + if (scale > 1.0f) { + trimArea = CGRectMake(trimArea.origin.x * scale, + trimArea.origin.y * scale, + trimArea.size.width * scale, + trimArea.size.height * scale); + } + shadowImageRef = CGImageCreateWithImageInRect(leftImg.CGImage, trimArea); + shadowImageLeft = [UIImage imageWithCGImage:shadowImageRef scale:scale orientation:UIImageOrientationUp]; + shadowImageRight = [UIImage imageWithCGImage:shadowImageRef scale:scale orientation:UIImageOrientationUpMirrored]; + CGImageRelease(shadowImageRef); + x += shadowImageLeft.size.width; + + y = 1; + while (y + shadowImageLeft.size.height < rectSize.height) { + [shadowImageLeft drawAtPoint:CGPointMake(centerPos - x, y)]; + [shadowImageRight drawAtPoint:CGPointMake(centerPos + x - shadowImageLeft.size.width, y)]; + y += shadowImageRight.size.height; + } + y = rectSize.height - shadowImageLeft.size.height; + [shadowImageLeft drawAtPoint:CGPointMake(centerPos - x, y)]; + [shadowImageRight drawAtPoint:CGPointMake(centerPos + x - shadowImageLeft.size.width, y)]; + + // Fill the body area with WHITE color + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetAllowsAntialiasing(context, true); + CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); + + if (isTextMode) { + + if (snippet) { + CGContextFillRect(context, CGRectMake(centerPos - x + 5, 4, rectSize.width - (centerPos - x + 7), rectSize.height - 16)); + } else { + CGContextFillRect(context, CGRectMake(centerPos - x + 5, 0, rectSize.width - (centerPos - x + 7), rectSize.height - 11)); + } + } else { + CGContextFillRect(context, CGRectMake(centerPos - x + 5, 4, rectSize.width - (centerPos - x + 5), rectSize.height - 16)); + } + + //-------------------------------- + // text-align: left/center/right + //-------------------------------- + NSTextAlignment textAlignment = NSTextAlignmentLeft; + if (styles && [styles objectForKey:@"text-align"]) { + NSString *textAlignValue = [styles objectForKey:@"text-align"]; + + NSDictionary *aligments = [NSDictionary dictionaryWithObjectsAndKeys: + ^() {return NSTextAlignmentLeft; }, @"left", + ^() {return NSTextAlignmentRight; }, @"right", + ^() {return NSTextAlignmentCenter; }, @"center", + nil]; + + typedef NSTextAlignment (^CaseBlock)(); + CaseBlock caseBlock = aligments[textAlignValue]; + if (caseBlock) { + textAlignment = caseBlock(); + } + } + + //------------------------------------- + // Draw the contents + //------------------------------------- + if (isTextMode) { + //Draw the title strings + if (title) { + UIColor *titleColor = [UIColor blackColor]; + if (styles && [styles objectForKey:@"color"]) { + titleColor = [[styles valueForKey:@"color"] parsePluginColor]; + } + + CGRect textRect = CGRectMake(5, 5 , rectSize.width - 10, textSize.height ); + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + style.lineBreakMode = NSLineBreakByWordWrapping; + style.alignment = textAlignment; + + NSDictionary *attributes = @{ + NSForegroundColorAttributeName : titleColor, + NSFontAttributeName : titleFont, + NSParagraphStyleAttributeName : style + }; + [title drawInRect:textRect + withAttributes:attributes]; + //CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 0.5); + //CGContextStrokeRect(context, textRect); + } + + //Draw the snippet + if (snippet) { + CGRect textRect = CGRectMake(5, textSize.height + 10 , rectSize.width - 10, snippetSize.height ); + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + style.lineBreakMode = NSLineBreakByWordWrapping; + style.alignment = textAlignment; + + NSDictionary *attributes = @{ + NSForegroundColorAttributeName : [UIColor grayColor], + NSFontAttributeName : snippetFont, + NSParagraphStyleAttributeName : style + }; + [snippet drawInRect:textRect withAttributes:attributes]; + } + } else { + //Draw the content image + CGRect imageRect = CGRectMake((rectSize.width - base64Image.size.width) / 2 , + -1 * ((rectSize.height - base64Image.size.height - 20) / 2 + 7.5), + base64Image.size.width, base64Image.size.height); + CGContextTranslateCTM(context, 0, base64Image.size.height); + CGContextScaleCTM(context, 1.0, -1.0); + CGContextDrawImage(context, imageRect, base64Image.CGImage); + } + + //------------------------------------- + // Generate new image + //------------------------------------- + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, rectSize.width, rectSize.height)]; + [imageView setContentMode:UIViewContentModeScaleAspectFill]; + [imageView setImage:image]; + return imageView; +} + +-(UIImage *)loadImageFromGoogleMap:(NSString *)fileName { + NSString *imagePath = [[NSBundle bundleWithIdentifier:@"com.google.GoogleMaps"] pathForResource:fileName ofType:@"png"]; + return [[UIImage alloc] initWithContentsOfFile:imagePath]; +} + + +- (void) didChangeActiveBuilding: (GMSIndoorBuilding *)building { + if (building == nil) { + return; + } + //Notify to the JS + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: 'indoor_building_focused', callback: '_onMapEvent'});}", + self.overlayId, self.overlayId]; + [self execJS:jsString]; +} + +- (void) didChangeActiveLevel: (GMSIndoorLevel *)activeLevel { + + if (activeLevel == nil) { + return; + } + GMSIndoorBuilding *building = self.map.indoorDisplay.activeBuilding; + + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + NSUInteger activeLevelIndex = [building.levels indexOfObject:activeLevel]; + [result setObject:[NSNumber numberWithInteger:activeLevelIndex] forKey:@"activeLevelIndex"]; + [result setObject:[NSNumber numberWithInteger:building.defaultLevelIndex] forKey:@"defaultLevelIndex"]; + + GMSIndoorLevel *level; + NSMutableDictionary *levelInfo; + NSMutableArray *levels = [NSMutableArray array]; + for (level in building.levels) { + levelInfo = [NSMutableDictionary dictionary]; + + [levelInfo setObject:[NSString stringWithString:level.name] forKey:@"name"]; + [levelInfo setObject:[NSString stringWithString:level.shortName] forKey:@"shortName"]; + [levels addObject:levelInfo]; + } + [result setObject:levels forKey:@"levels"]; + + NSError *error; + NSData *data = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:&error]; + + + NSString *JSONstring = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + + //Notify to the JS + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: 'indoor_level_activated', callback: '_onMapEvent', args: [%@]});}", + self.overlayId, self.overlayId, JSONstring]; + + [self execJS:jsString]; +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarker.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarker.h new file mode 100644 index 0000000..bf4615c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarker.h @@ -0,0 +1,51 @@ +// +// PluginMarker.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" +#import "PluginUtil.h" +#import "UIImageCache.h" + +@interface PluginMarker : CDVPlugin + +@property (nonatomic) BOOL initialized; + +@property (nonatomic, strong) PluginMapViewController* mapCtrl; +- (void)create:(CDVInvokedUrlCommand*)command; +- (void)showInfoWindow:(CDVInvokedUrlCommand*)command; +- (void)hideInfoWindow:(CDVInvokedUrlCommand*)command; +- (void)getPosition:(CDVInvokedUrlCommand*)command; +- (void)setSnippet:(CDVInvokedUrlCommand*)command; +- (void)setTitle:(CDVInvokedUrlCommand*)command; +- (void)setFlat:(CDVInvokedUrlCommand*)command; +- (void)setOpacity:(CDVInvokedUrlCommand*)command; +- (void)setDraggable:(CDVInvokedUrlCommand*)command; +- (void)setDisableAutoPan:(CDVInvokedUrlCommand*)command; +- (void)setVisible:(CDVInvokedUrlCommand*)command; +- (void)setIcon:(CDVInvokedUrlCommand*)command; +- (void)setIconAnchor:(CDVInvokedUrlCommand*)command; +- (void)setInfoWindowAnchor:(CDVInvokedUrlCommand*)command; +- (void)remove:(CDVInvokedUrlCommand*)command; +- (void)setPosition:(CDVInvokedUrlCommand*)command; +- (void)setRotation:(CDVInvokedUrlCommand*)command; +- (void)setAnimation:(CDVInvokedUrlCommand*)command; +- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock; + + +// Internal use +/* +-(void)setMarkerAnimation_:(NSString *)animation marker:(GMSMarker *)marker pluginResult:(CDVPluginResult *)pluginResult callbackId:(NSString*)callbackId; +-(void)setDropAnimation_:(GMSMarker *)marker pluginResult:(CDVPluginResult *)pluginResult callbackId:(NSString*)callbackId; +-(void)setBounceAnimation_:(GMSMarker *)marker pluginResult:(CDVPluginResult *)pluginResult callbackId:(NSString*)callbackId; +*/ +- (void)_create:(NSString *)markerId markerOptions:(NSDictionary *)json callbackBlock:(void (^)(BOOL successed, id resultObj)) callbackBlock; +-(void)setIcon_:(GMSMarker *)marker iconProperty:(NSDictionary *)iconProperty callbackBlock:(void (^)(BOOL successed, id resultObj)) callbackBlock; +-(void)_removeMarker:(GMSMarker *)marker; + + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarker.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarker.m new file mode 100644 index 0000000..f06e4c6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarker.m @@ -0,0 +1,1535 @@ +// +// PluginMarker.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginMarker.h" +@implementation PluginMarker +-(void)setPluginViewController:(PluginViewController *)viewCtrl +{ + self.mapCtrl = (PluginMapViewController *)viewCtrl; +} + +- (void)pluginInitialize +{ + if (self.initialized) { + return; + } + self.initialized = YES; + [super pluginInitialize]; + // Initialize this plugin +} + +- (void)pluginUnload +{ + + // Plugin destroy + NSArray *keys = [self.mapCtrl.objects allKeys]; + NSString *key; + for (int i = 0; i < [keys count]; i++) { + key = [keys objectAtIndex:i]; + if ([key hasPrefix:@"marker_"] && + ![key hasPrefix:@"marker_icon_"] && + ![key hasPrefix:@"marker_property"]) { + GMSMarker *marker = (GMSMarker *)[self.mapCtrl.objects objectForKey:key]; + if (marker) { + marker.userData = nil; + marker.map = nil; + [marker.layer removeAllAnimations]; + } + marker = nil; + } + [self.mapCtrl.objects removeObjectForKey:key]; + } + + [[UIImageCache sharedInstance] removeAllCachedImages]; + key = nil; + keys = nil; + + + NSString *pluginId = [NSString stringWithFormat:@"%@-marker", self.mapCtrl.overlayId]; + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + [cdvViewController.pluginObjects removeObjectForKey:pluginId]; + //[cdvViewController.pluginsMap setValue:nil forKey:pluginId]; + pluginId = nil; +} + +/** + * @param marker options + * @return marker key + */ +-(void)create:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSDictionary *json = [command.arguments objectAtIndex:1]; + NSString *hashCode = [command.arguments objectAtIndex:2]; + + __block NSMutableDictionary *createResult = [[NSMutableDictionary alloc] init]; + NSString *markerId = [NSString stringWithFormat:@"marker_%@", hashCode]; + [createResult setObject:markerId forKey:@"__pgmId"]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + CDVCommandDelegateImpl *cmdDelegate = (CDVCommandDelegateImpl *)self.commandDelegate; + [self _create:markerId markerOptions:json callbackBlock:^(BOOL successed, id result) { + CDVPluginResult* pluginResult; + + if (successed == NO) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:result]; + } else { + GMSMarker *marker = result; + NSString *iconCacheKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData]; + UIImage *image = [[UIImageCache sharedInstance] getCachedImageForKey:iconCacheKey]; + if (image != nil) { + [createResult setObject:[NSNumber numberWithInt: (int)image.size.width] forKey:@"width"]; + [createResult setObject:[NSNumber numberWithInt: (int)image.size.height] forKey:@"height"]; + } else { + [createResult setObject:[NSNumber numberWithInt: 24] forKey:@"width"]; + [createResult setObject:[NSNumber numberWithInt: 40] forKey:@"height"]; + } + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:createResult ]; + } + [cmdDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; + }]; + +} +- (void)_create:(NSString *)markerId markerOptions:(NSDictionary *)json callbackBlock:(void (^)(BOOL successed, id resultObj)) callbackBlock { + + if ([NSThread isMainThread] == NO) { + callbackBlock(NO, @"PluginMarker._create() must run on the main thread."); + return; + } + + NSMutableDictionary *iconProperty = nil; + NSString *animation = nil; + NSDictionary *latLng = [json objectForKey:@"position"]; + double latitude = [[latLng valueForKey:@"lat"] doubleValue]; + double longitude = [[latLng valueForKey:@"lng"] doubleValue]; + + CLLocationCoordinate2D position = CLLocationCoordinate2DMake(latitude, longitude); + + // Create a marker + GMSMarker *marker = [GMSMarker markerWithPosition:position]; + marker.tracksViewChanges = NO; + marker.tracksInfoWindowChanges = NO; + + if ([json valueForKey:@"title"] && [json valueForKey:@"title"] != [NSNull null]) { + [marker setTitle: [json valueForKey:@"title"]]; + } + if ([json valueForKey:@"snippet"] && [json valueForKey:@"snippet"] != [NSNull null]) { + [marker setSnippet: [json valueForKey:@"snippet"]]; + } + if ([json valueForKey:@"draggable"] && [json valueForKey:@"draggable"] != [NSNull null]) { + [marker setDraggable:[[json valueForKey:@"draggable"] boolValue]]; + } + if ([json valueForKey:@"flat"] && [json valueForKey:@"flat"] != [NSNull null]) { + [marker setFlat:[[json valueForKey:@"flat"] boolValue]]; + } + if ([json valueForKey:@"rotation"] && [json valueForKey:@"rotation"] != [NSNull null]) { + CLLocationDegrees degrees = [[json valueForKey:@"rotation"] doubleValue]; + [marker setRotation:degrees]; + } + if ([json valueForKey:@"opacity"] && [json valueForKey:@"opacity"] != [NSNull null]) { + [marker setOpacity:[[json valueForKey:@"opacity"] floatValue]]; + } + if ([json valueForKey:@"zIndex"] && [json valueForKey:@"zIndex"] != [NSNull null]) { + [marker setZIndex:[[json valueForKey:@"zIndex"] intValue]]; + } + + [self.mapCtrl.objects setObject:marker forKey: markerId]; + marker.userData = markerId; + + // Custom properties + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + NSString *propertyId = [NSString stringWithFormat:@"marker_property_%@", markerId]; + + if ([json valueForKey:@"styles"] && [json valueForKey:@"styles"] != [NSNull null]) { + NSDictionary *styles = [json valueForKey:@"styles"]; + [properties setObject:styles forKey:@"styles"]; + } + + BOOL disableAutoPan = NO; + if ([json valueForKey:@"disableAutoPan"] != nil && [json valueForKey:@"disableAutoPan"] != [NSNull null]) { + disableAutoPan = [[json valueForKey:@"disableAutoPan"] boolValue]; + } + [properties setObject:[NSNumber numberWithBool:disableAutoPan] forKey:@"disableAutoPan"]; + + [self.mapCtrl.objects setObject:properties forKey: propertyId]; + + // Create icon + NSObject *icon = [json valueForKey:@"icon"]; + if ([icon isKindOfClass:[NSString class]]) { + iconProperty = [NSMutableDictionary dictionary]; + [iconProperty setObject:icon forKey:@"url"]; + + } else if ([icon isKindOfClass:[NSDictionary class]]) { + iconProperty = [json valueForKey:@"icon"]; + + id url = [iconProperty objectForKey:@"url"]; + if ([url isKindOfClass:[NSArray class]]) { + NSArray *rgbColor = url; + iconProperty = [[NSMutableDictionary alloc] init]; + [iconProperty setObject:[rgbColor parsePluginColor] forKey:@"iconColor"]; + } + + } else if ([icon isKindOfClass:[NSArray class]]) { + NSArray *rgbColor = [json valueForKey:@"icon"]; + iconProperty = [NSMutableDictionary dictionary]; + [iconProperty setObject:[rgbColor parsePluginColor] forKey:@"iconColor"]; + } + + // Visible property + NSString *visibleValue = [NSString stringWithFormat:@"%@", json[@"visible"]]; + BOOL visible = YES; + if ([@"0" isEqualToString:visibleValue]) { + // false + visible = NO; + if (iconProperty == nil) { + iconProperty = [NSMutableDictionary dictionary]; + } + [iconProperty setObject:[NSNumber numberWithBool:false] forKey:@"visible"]; + } else { + // default or true + if (iconProperty == nil) { + iconProperty = [NSMutableDictionary dictionary]; + } + [iconProperty setObject:[NSNumber numberWithBool:true] forKey:@"visible"]; + } + + // Animation + if ([json valueForKey:@"animation"] && [json valueForKey:@"animation"] != [NSNull null]) { + animation = [json valueForKey:@"animation"]; + if (iconProperty == nil) { + iconProperty = [NSMutableDictionary dictionary]; + } + [iconProperty setObject:animation forKey:@"animation"]; + //NSLog(@"--->animation = %@", animation); + } + + if ([json valueForKey:@"infoWindowAnchor"] && [json valueForKey:@"infoWindowAnchor"] != [NSNull null]) { + [iconProperty setObject:[json valueForKey:@"infoWindowAnchor"] forKey:@"infoWindowAnchor"]; + } + if (iconProperty && ([iconProperty objectForKey:@"url"] || [iconProperty objectForKey:@"iconColor"])) { + + // Load icon in asynchronise + [self setIcon_:marker iconProperty:iconProperty callbackBlock:callbackBlock]; + } else { + if (visible) { + marker.map = self.mapCtrl.map; + } else { + marker.map = nil; + } + + + if (animation) { + [self setMarkerAnimation_:animation marker:marker callbackBlock:^(void) { + callbackBlock(YES, marker); + }]; + } else { + callbackBlock(YES, marker); + } + } +} + +/** + * Show the infowindow of the current marker + * @params markerId + */ +-(void)showInfoWindow:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *hashCode = [command.arguments objectAtIndex:0]; + + GMSMarker *marker = [self.mapCtrl.objects objectForKey:hashCode]; + if (marker) { + self.mapCtrl.map.selectedMarker = marker; + self.mapCtrl.activeMarker = marker; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +/** + * Hide current infowindow + * @params markerId + */ +-(void)hideInfoWindow:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + self.mapCtrl.map.selectedMarker = nil; + self.mapCtrl.activeMarker = nil; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +/** + * @params markerId + * @return current marker position with array(latitude, longitude) + */ +-(void)getPosition:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + NSNumber *latitude = @0.0; + NSNumber *longitude = @0.0; + if (marker) { + latitude = [NSNumber numberWithFloat: marker.position.latitude]; + longitude = [NSNumber numberWithFloat: marker.position.longitude]; + } + NSMutableDictionary *json = [NSMutableDictionary dictionary]; + [json setObject:latitude forKey:@"lat"]; + [json setObject:longitude forKey:@"lng"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:json]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +/** + * Set title to the specified marker + * @params markerId + */ +-(void)setTitle:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + marker.title = [command.arguments objectAtIndex:1]; + + NSString *propertyId = [NSString stringWithFormat:@"marker_property_%@", markerId]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + + +/** + * Set title to the specified marker + * @params markerId + */ +-(void)setSnippet:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + marker.snippet = [command.arguments objectAtIndex:1]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Remove the specified marker + * @params markerId + */ +-(void)remove:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + [self.mapCtrl.objects removeObjectForKey:markerId]; + [self _removeMarker:marker]; + marker = nil; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +-(void)_removeMarker:(GMSMarker *)marker { + if (marker == nil || marker.userData == nil) { + return; + } + + @synchronized (self.mapCtrl.objects) { + NSString *iconCacheKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData]; + NSString *propertyId = [NSString stringWithFormat:@"marker_property_%@", marker.userData]; + [self.mapCtrl.objects objectForKey: marker.userData]; + //[self.mapCtrl.objects removeObjectForKey:iconCacheKey]; + [self.mapCtrl.objects removeObjectForKey:propertyId]; + marker.map = nil; + marker = nil; + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + @synchronized(self.mapCtrl.objects) { + if ([self.mapCtrl.objects objectForKey:iconCacheKey]) { + + NSString *cacheKey = [self.mapCtrl.objects objectForKey:iconCacheKey]; + + if ([[UIImageCache sharedInstance].iconCacheKeys objectForKey:cacheKey]) { + int count = [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:cacheKey] intValue]; + count--; + if (count < 1) { + [[UIImageCache sharedInstance] removeCachedImageForKey:cacheKey]; + [[UIImageCache sharedInstance].iconCacheKeys removeObjectForKey:cacheKey]; + } else { + [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:count] forKey:cacheKey]; + } + } + + if ([self.mapCtrl.objects objectForKey:iconCacheKey]) { + [self.mapCtrl.objects removeObjectForKey:iconCacheKey]; + } + } + } + }]; + } +} + +/** + * Set anchor of the marker + * @params markerId + */ +-(void)setIconAnchor:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + CGFloat anchorX = [[command.arguments objectAtIndex:1] floatValue]; + CGFloat anchorY = [[command.arguments objectAtIndex:2] floatValue]; + + if (marker.icon) { + anchorX = anchorX / marker.icon.size.width; + anchorY = anchorY / marker.icon.size.height; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [marker setGroundAnchor:CGPointMake(anchorX, anchorY)]; + }]; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +/** + * Set anchor of the info window + * @params markerId + */ +-(void)setInfoWindowAnchor:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + float anchorX = [[command.arguments objectAtIndex:1] floatValue]; + float anchorY = [[command.arguments objectAtIndex:2] floatValue]; + CDVPluginResult* pluginResult; + if (marker.icon) { + anchorX = anchorX / marker.icon.size.width; + anchorY = anchorY / marker.icon.size.height; + [marker setInfoWindowAnchor:CGPointMake(anchorX, anchorY)]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + } + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; +} + + +/** + * Set opacity + * @params markerId + */ +-(void)setOpacity:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + marker.opacity = [[command.arguments objectAtIndex:1] floatValue]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set zIndex + * @params markerId + */ +-(void)setZIndex:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + marker.zIndex = [[command.arguments objectAtIndex:1] intValue]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set draggable + * @params markerId + */ +-(void)setDraggable:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + Boolean isEnabled = [[command.arguments objectAtIndex:1] boolValue]; + [marker setDraggable:isEnabled]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set disable auto pan + * @params markerId + */ +-(void)setDisableAutoPan:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + BOOL disableAutoPan = [[command.arguments objectAtIndex:1] boolValue]; + + NSString *propertyId = [NSString stringWithFormat:@"marker_property_%@",markerId]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:disableAutoPan] forKey:@"disableAutoPan"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + NSLog(@"--->propertyId = %@", propertyId); + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +/** + * Set visibility + * @params markerId + */ +-(void)setVisible:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + Boolean isVisible = [[command.arguments objectAtIndex:1] boolValue]; + + if (isVisible) { + marker.map = self.mapCtrl.map; + } else { + marker.map = nil; + } + + NSString *propertyId = [NSString stringWithFormat:@"marker_property_%@", markerId]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"visible"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set position + * @params key + */ +-(void)setPosition:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + + double latitude = [[command.arguments objectAtIndex:1] doubleValue]; + double longitude = [[command.arguments objectAtIndex:2] doubleValue]; + CLLocationCoordinate2D position = CLLocationCoordinate2DMake(latitude, longitude); + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [marker setPosition:position]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set flattable + * @params markerId + */ +-(void)setFlat:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + Boolean isFlat = [[command.arguments objectAtIndex:1] boolValue]; + [marker setFlat: isFlat]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * set icon + * @params markerId + */ +-(void)setIcon:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + if (marker == nil) { + NSLog(@"--> can not find the maker : %@", markerId); + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR + messageAsString:[NSString stringWithFormat:@"Cannot find the marker : %@", markerId]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + + // Create icon + NSMutableDictionary *iconProperty; + id icon = [command.arguments objectAtIndex:1]; + if ([icon isKindOfClass:[NSString class]]) { + iconProperty = [[NSMutableDictionary alloc] init]; + [iconProperty setObject:icon forKey:@"url"]; + } else if ([icon isKindOfClass:[NSDictionary class]]) { + iconProperty = [command.arguments objectAtIndex:1]; + + id url = [iconProperty objectForKey:@"url"]; + if ([url isKindOfClass:[NSArray class]]) { + NSArray *rgbColor = url; + iconProperty = [[NSMutableDictionary alloc] init]; + [iconProperty setObject:[rgbColor parsePluginColor] forKey:@"iconColor"]; + } + } else if ([icon isKindOfClass:[NSArray class]]) { + NSArray *rgbColor = icon; + iconProperty = [[NSMutableDictionary alloc] init]; + [iconProperty setObject:[rgbColor parsePluginColor] forKey:@"iconColor"]; + } + + CDVCommandDelegateImpl *cmdDelegate = (CDVCommandDelegateImpl *)self.commandDelegate; + [self setIcon_:marker iconProperty:iconProperty callbackBlock:^(BOOL successed, id resultObj) { + CDVPluginResult* pluginResult; + if (successed == NO) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:resultObj]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } + [cmdDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +/** + * set rotation + */ +-(void)setRotation:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + + CLLocationDegrees degrees = [[command.arguments objectAtIndex:1] doubleValue]; + [marker setRotation:degrees]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + + +-(void)setAnimation:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *markerId = [command.arguments objectAtIndex:0]; + GMSMarker *marker = [self.mapCtrl.objects objectForKey:markerId]; + + NSString *animation = [command.arguments objectAtIndex:1]; + CDVCommandDelegateImpl *cmdDelegate = (CDVCommandDelegateImpl *)self.commandDelegate; + + [self setMarkerAnimation_:animation marker:marker callbackBlock:^(void) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [cmdDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + }]; +} + +-(void)setMarkerAnimation_:(NSString *)animation marker:(GMSMarker *)marker callbackBlock:(void (^)()) callbackBlock { + + animation = [animation uppercaseString]; + SWITCH(animation) { + CASE (@"DROP") { + [self setDropAnimation_:marker callbackBlock:callbackBlock]; + break; + } + CASE (@"BOUNCE") { + [self setBounceAnimation_:marker callbackBlock:callbackBlock]; + break; + } + DEFAULT { + callbackBlock(); + break; + } + } +} + +/** + * set animation + * (memo) http://stackoverflow.com/a/19316475/697856 + * (memo) http://qiita.com/edo_m18/items/4309d01b67ee42c35b3c + * (memo) http://stackoverflow.com/questions/12164049/animationdidstop-for-group-animation + */ +-(void)setDropAnimation_:(GMSMarker *)marker callbackBlock:(void (^)()) callbackBlock { + /** + * Marker drop animation + */ + int duration = 0.1f; + + CAKeyframeAnimation *longitudeAnim = [CAKeyframeAnimation animationWithKeyPath:@"longitude"]; + CAKeyframeAnimation *latitudeAnim = [CAKeyframeAnimation animationWithKeyPath:@"latitude"]; + + GMSProjection *projection = self.mapCtrl.map.projection; + CGPoint point = [projection pointForCoordinate:marker.position]; + double distance = point.y ; + + NSMutableArray *latitudePath = [NSMutableArray array]; + NSMutableArray *longitudeath = [NSMutableArray array]; + CLLocationCoordinate2D startLatLng; + + point.y = 0; + startLatLng = [projection coordinateForPoint:point]; + [latitudePath addObject:[NSNumber numberWithDouble:startLatLng.latitude]]; + [longitudeath addObject:[NSNumber numberWithDouble:startLatLng.longitude]]; + + point.y = distance; + startLatLng = [projection coordinateForPoint:point]; + [latitudePath addObject:[NSNumber numberWithDouble:startLatLng.latitude]]; + [longitudeath addObject:[NSNumber numberWithDouble:startLatLng.longitude]]; + + longitudeAnim.values = longitudeath; + latitudeAnim.values = latitudePath; + + CAAnimationGroup *group = [[CAAnimationGroup alloc] init]; + group.animations = @[longitudeAnim, latitudeAnim]; + group.duration = duration; + [group setCompletionBlock: callbackBlock]; + + [marker.layer addAnimation:group forKey:@"dropMarkerAnim"]; + +} +-(void)setBounceAnimation_:(GMSMarker *)marker callbackBlock:(void (^)()) callbackBlock { + /** + * Marker bounce animation + */ + int duration = 1; + + CAKeyframeAnimation *longitudeAnim = [CAKeyframeAnimation animationWithKeyPath:@"longitude"]; + CAKeyframeAnimation *latitudeAnim = [CAKeyframeAnimation animationWithKeyPath:@"latitude"]; + + GMSProjection *projection = self.mapCtrl.map.projection; + CGPoint point = [projection pointForCoordinate:marker.position]; + double distance = point.y; + + NSMutableArray *latitudePath = [NSMutableArray array]; + NSMutableArray *longitudeath = [NSMutableArray array]; + CLLocationCoordinate2D startLatLng; + + point.y = distance * 0.5f; + + for (double i = 0.5f; i > 0; i-= 0.15f) { + startLatLng = [projection coordinateForPoint:point]; + [latitudePath addObject:[NSNumber numberWithDouble:startLatLng.latitude]]; + [longitudeath addObject:[NSNumber numberWithDouble:startLatLng.longitude]]; + + point.y = distance; + startLatLng = [projection coordinateForPoint:point]; + [latitudePath addObject:[NSNumber numberWithDouble:startLatLng.latitude]]; + [longitudeath addObject:[NSNumber numberWithDouble:startLatLng.longitude]]; + + point.y = distance - distance * (i - 0.15f); + } + longitudeAnim.values = longitudeath; + latitudeAnim.values = latitudePath; + + CAAnimationGroup *group = [[CAAnimationGroup alloc] init]; + group.animations = @[longitudeAnim, latitudeAnim]; + group.duration = duration; + [group setCompletionBlock: callbackBlock]; + + [marker.layer addAnimation:group forKey:@"bounceMarkerAnim"]; +} + +/** + * @private + * Load the icon; then set to the marker + */ + +-(void)setIcon_:(GMSMarker *)marker iconProperty:(NSDictionary *)iconProperty callbackBlock:(void (^)(BOOL successed, id resultObj)) callbackBlock { + + if (marker == nil) { + callbackBlock(NO, @"marker is null"); + return; + } + + NSString *iconPath = nil; + CGFloat width = -1; + CGFloat height = -1; + + // `url` property + iconPath = [iconProperty valueForKey:@"url"]; + if (self.mapCtrl.debuggable) { + NSLog(@"---- setIcon_ : %@", iconPath); + } + + // `animation` property + NSString *animationValue = nil; + if ([iconProperty valueForKey:@"animation"] && [iconProperty valueForKey:@"animation"] != [NSNull null]) { + animationValue = [iconProperty valueForKey:@"animation"]; + } + __block NSString *animation = animationValue; + + //-------------------------------- + // the icon property is color name + //-------------------------------- + if ([iconProperty valueForKey:@"iconColor"] && [iconProperty valueForKey:@"iconColor"] != [NSNull null]) { + dispatch_async(dispatch_get_main_queue(), ^{ + UIColor *iconColor = [iconProperty valueForKey:@"iconColor"]; + marker.icon = [GMSMarker markerImageWithColor:iconColor]; + + // The `visible` property + if (iconProperty[@"visible"] == [NSNumber numberWithBool:true]) { + marker.map = self.mapCtrl.map; + } else if (iconProperty[@"visible"] == [NSNumber numberWithBool:false]) { + marker.map = nil; + } + + if (animation) { + // Do animation, then send the result + [self setMarkerAnimation_:animation marker:marker callbackBlock:^(void) { + callbackBlock(YES, marker); + }]; + } else { + // Send the result + callbackBlock(YES, marker); + } + }); + return; + } + + if (iconPath == nil) { + callbackBlock(YES, marker); + //callbackBlock(NO, @"icon property is null"); + return; + } + + // `size` property + if ([iconProperty valueForKey:@"size"] && [iconProperty valueForKey:@"size"] != [NSNull null]) { + NSDictionary *size = [iconProperty valueForKey:@"size"]; + width = [[size objectForKey:@"width"] floatValue]; + height = [[size objectForKey:@"height"] floatValue]; + } + + + + + + if (self.mapCtrl.debuggable) { + NSLog(@"iconPath = %@", iconPath); + } + NSString *iconCacheKey = [NSString stringWithFormat:@"%lu/%d,%d", (unsigned long)iconPath.hash, (int)width, (int)height]; + UIImage *image = [[UIImageCache sharedInstance] getCachedImageForKey:iconCacheKey]; + + + + //--------------------------------------- + // If the image is cached, return it + //--------------------------------------- + if (image != nil) { + + NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData]; + + // Cache the icon image + if ([self.mapCtrl.objects objectForKey:iconKey]) { + NSString *currentCacheKey = [self.mapCtrl.objects objectForKey:iconKey]; + if (currentCacheKey && ![iconCacheKey isEqualToString:currentCacheKey]) { + + int count = [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:currentCacheKey] intValue]; + count--; + if (count < 1) { + [[UIImageCache sharedInstance] removeCachedImageForKey:currentCacheKey]; + [[UIImageCache sharedInstance].iconCacheKeys removeObjectForKey:currentCacheKey]; + } else { + [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:count] forKey:currentCacheKey]; + } + //NSLog(@"---> currentKey = %@, count = %d", iconKey, count); + } + + } + + [self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey]; + int count = [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:iconCacheKey] intValue]; + count++; + [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:count] forKey:iconCacheKey]; + + //NSLog(@"--->iconCacheKey = %@, count = %d", iconCacheKey, count); + + if ([iconProperty objectForKey:@"label"]) { + image = [self drawLabel:image labelOptions:[iconProperty objectForKey:@"label"]]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + marker.icon = image; + + CGFloat anchorX = 0; + CGFloat anchorY = 0; + // The `anchor` property for the icon + if ([iconProperty valueForKey:@"anchor"] && [iconProperty valueForKey:@"anchor"] != [NSNull null]) { + NSArray *points = [iconProperty valueForKey:@"anchor"]; + anchorX = [[points objectAtIndex:0] floatValue] / image.size.width; + anchorY = [[points objectAtIndex:1] floatValue] / image.size.height; + marker.groundAnchor = CGPointMake(anchorX, anchorY); + } + + // The `infoWindowAnchor` property + if ([iconProperty valueForKey:@"infoWindowAnchor"] && [iconProperty valueForKey:@"infoWindowAnchor"] != [NSNull null]) { + NSArray *points = [iconProperty valueForKey:@"infoWindowAnchor"]; + anchorX = [[points objectAtIndex:0] floatValue] / image.size.width; + anchorY = [[points objectAtIndex:1] floatValue] / image.size.height; + marker.infoWindowAnchor = CGPointMake(anchorX, anchorY); + } + + + // The `visible` property + if (iconProperty[@"visible"] == [NSNumber numberWithBool:true]) { + marker.map = self.mapCtrl.map; + } else if (iconProperty[@"visible"] == [NSNumber numberWithBool:false]) { + marker.map = nil; + } + + if (animation) { + // Do animation, then send the result + [self setMarkerAnimation_:animation marker:marker callbackBlock:^(void) { + callbackBlock(YES, marker); + }]; + } else { + // Send the result + callbackBlock(YES, marker); + } + }); + + return; + } + + // + // Load icon from file or Base64 encoded strings + // + + if ([iconPath rangeOfString:@"data:image/"].location != NSNotFound && + [iconPath rangeOfString:@";base64,"].location != NSNotFound) { + + NSArray *tmp = [iconPath componentsSeparatedByString:@","]; + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:[tmp objectAtIndex:1] options:0]; + + image = [[UIImage alloc] initWithData:decodedData]; + if (width && height) { + image = [image resize:width height:height]; + } + } + + NSRange range; + if (image == nil && ![iconPath hasPrefix:@"http"]) { + + if (![iconPath containsString:@"://"] && + ![iconPath hasPrefix:@"/"] && + ![iconPath hasPrefix:@"www"] && + ![iconPath hasPrefix:@"./"] && + ![iconPath hasPrefix:@"../"]) { + iconPath = [NSString stringWithFormat:@"./%@", iconPath]; + } + + if ([iconPath hasPrefix:@"./"] || [iconPath hasPrefix:@"../"]) { + NSError *error = nil; + + // replace repeated "./" (i.e ./././test.png) + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(\\.\\/)+" options:NSRegularExpressionCaseInsensitive error:&error]; + iconPath = [regex stringByReplacingMatchesInString:iconPath options:0 range:NSMakeRange(0, [iconPath length]) withTemplate:@"./"]; + + // Get the current URL, then calculate the relative path. + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + + id webview = cdvViewController.webView; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSURL *url = [webview URL]; + NSString *currentURL = url.absoluteString; + //NSLog(@"currentURL = %@", url); + if (![[url lastPathComponent] isEqualToString:@"/"]) { + currentURL = [currentURL stringByDeletingLastPathComponent]; + } + // remove page unchor (i.e index.html#page=test, index.html?key=value) + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[#\\?].*$" options:NSRegularExpressionCaseInsensitive error:nil]; + currentURL = [regex stringByReplacingMatchesInString:currentURL options:0 range:NSMakeRange(0, [currentURL length]) withTemplate:@""]; + + // remove file name (i.e /index.html) + regex = [NSRegularExpression regularExpressionWithPattern:@"\\/[^\\/]+\\.[^\\/]+$" options:NSRegularExpressionCaseInsensitive error:nil]; + currentURL = [regex stringByReplacingMatchesInString:currentURL options:0 range:NSMakeRange(0, [currentURL length]) withTemplate:@""]; + + //url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", currentURL, iconPath]]; + currentURL = [NSString stringWithFormat:@"%@/%@", currentURL, iconPath]; + currentURL = [currentURL regReplace:@"\\/\\.\\/" replaceTxt:@"/" options:0]; + currentURL = [currentURL regReplace:@"\\/+" replaceTxt:@"/" options:0]; + currentURL = [currentURL stringByReplacingOccurrencesOfString:@":/" withString:@"://"]; + currentURL = [currentURL stringByReplacingOccurrencesOfString:@":///" withString:@"://"]; + //NSLog(@"currentURL = %@", currentURL); + url = [NSURL URLWithString:currentURL]; + + // + // Load the icon from over the internet + // + [self downloadImageWithURL:url completionBlock:^(BOOL succeeded, UIImage *image) { + + if (!succeeded) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"[fail] url = %@", url); + // The `visible` property + if (iconProperty[@"visible"] == [NSNumber numberWithBool:true]) { + marker.map = self.mapCtrl.map; + } else if (iconProperty[@"visible"] == [NSNumber numberWithBool:false]) { + marker.map = nil; + } + if ([[UIImageCache sharedInstance].iconCacheKeys objectForKey:iconCacheKey]) { + [[UIImageCache sharedInstance].iconCacheKeys removeObjectForKey:iconCacheKey]; + } + + callbackBlock(NO, [NSString stringWithFormat:@"Can not load image from '%@'.", url]); + }); + return; + } + + + if (self.mapCtrl.debuggable) { + NSLog(@"[success] url = %@", url); + } + + if (width && height) { + image = [image resize:width height:height]; + } + + // Cache the icon image + NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData]; + [[UIImageCache sharedInstance] cacheImage:image forKey:iconCacheKey]; + [self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey]; + [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:1] forKey:iconCacheKey];; + //NSLog(@"--->confirm: key: %@, iconCacheKey : %@", iconKey, [self.mapCtrl.objects objectForKey:iconKey]); + + // Draw label + if ([iconProperty objectForKey:@"label"]) { + image = [self drawLabel:image labelOptions:[iconProperty objectForKey:@"label"]]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + marker.icon = image; + + // The `anchor` property for the icon + if ([iconProperty valueForKey:@"anchor"] && [iconProperty valueForKey:@"anchor"] != [NSNull null]) { + NSArray *points = [iconProperty valueForKey:@"anchor"]; + CGFloat anchorX = [[points objectAtIndex:0] floatValue] / image.size.width; + CGFloat anchorY = [[points objectAtIndex:1] floatValue] / image.size.height; + marker.groundAnchor = CGPointMake(anchorX, anchorY); + } + + + // The `infoWindowAnchor` property + if ([iconProperty valueForKey:@"infoWindowAnchor"] && [iconProperty valueForKey:@"infoWindowAnchor"] != [NSNull null]) { + NSArray *points = [iconProperty valueForKey:@"infoWindowAnchor"]; + CGFloat anchorX = [[points objectAtIndex:0] floatValue] / image.size.width; + CGFloat anchorY = [[points objectAtIndex:1] floatValue] / image.size.height; + marker.infoWindowAnchor = CGPointMake(anchorX, anchorY); + } + + // The `visible` property + if (iconProperty[@"visible"] == [NSNumber numberWithBool:true]) { + marker.map = self.mapCtrl.map; + } else if (iconProperty[@"visible"] == [NSNumber numberWithBool:false]) { + marker.map = nil; + } + + + if (animation) { + // Do animation, then send the result + [self setMarkerAnimation_:animation marker:marker callbackBlock:^(void) { + callbackBlock(YES, marker); + }]; + } else { + // Send the result + callbackBlock(YES, marker); + } + + }); + }]; + }]; + return; + } + + + range = [iconPath rangeOfString:@"cdvfile://"]; + if ([iconPath hasPrefix:@"cdvfile://"]) { + + iconPath = [PluginUtil getAbsolutePathFromCDVFilePath:self.webView cdvFilePath:iconPath]; + + if (iconPath == nil) { + if (self.mapCtrl.debuggable) { + NSLog(@"(debug)Can not convert '%@' to device full path.", iconPath); + } + //[self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId]; + callbackBlock(NO, [NSString stringWithFormat:@"Can not convert '%@' to device full path.", iconPath]); + return; + } + } + + + range = [iconPath rangeOfString:@"://"]; + + if (range.location == 0) { + /** + * Load the icon from local path + */ + + if ([iconPath hasPrefix:@"/"]) { + // Get the current URL, then calculate the relative path. + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + + id webview = cdvViewController.webView; + NSURL *url = [webview URL]; + NSString *currentURL = url.absoluteString; + currentURL = [currentURL stringByDeletingLastPathComponent]; + currentURL = [currentURL stringByReplacingOccurrencesOfString:@"file:" withString:@""]; + currentURL = [currentURL stringByReplacingOccurrencesOfString:@"//" withString:@"/"]; + currentURL = [currentURL stringByReplacingOccurrencesOfString:@"%20" withString:@" "]; + iconPath = [NSString stringWithFormat:@"file://%@/%@", currentURL, iconPath]; + } else { + iconPath = [NSString stringWithFormat:@"file://%@", iconPath]; + } + } + + if ([iconPath hasPrefix:@"file://"]) { + iconPath = [iconPath stringByReplacingOccurrencesOfString:@"file://" withString:@""]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:iconPath]) { + //if (self.mapCtrl.debuggable) { + NSLog(@"(error)There is no file at '%@'.", iconPath); + //} + //[self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId]; + callbackBlock(NO, [NSString stringWithFormat:@"There is no file at '%@'.", iconPath]); + return; + } + } + + image = [UIImage imageNamed:iconPath]; + } + + if (image != nil) { + + if (width && height) { + image = [image resize:width height:height]; + } + + + + // Cache the icon image + NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData]; + if ([self.mapCtrl.objects objectForKey:iconKey]) { + NSString *currentCacheKey = [self.mapCtrl.objects objectForKey:iconKey]; + if (currentCacheKey && ![iconCacheKey isEqualToString:currentCacheKey]) { + + int count = [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:currentCacheKey] intValue]; + count--; + if (count < 1) { + [[UIImageCache sharedInstance] removeCachedImageForKey:currentCacheKey]; + [[UIImageCache sharedInstance].iconCacheKeys removeObjectForKey:currentCacheKey]; + } else { + [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:count] forKey:currentCacheKey]; + } + //NSLog(@"---> currentKey = %@, count = %d", iconKey, count); + } + + } + + [[UIImageCache sharedInstance] cacheImage:image forKey:iconCacheKey]; + [self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey]; + [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:1] forKey:iconCacheKey]; + //NSLog(@"--->(save)iconCacheKey: key: %@ = %d", iconCacheKey, [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:iconCacheKey] intValue]); + + + //NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData]; + //[self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey]; + + + + + if ([iconProperty objectForKey:@"label"]) { + image = [self drawLabel:image labelOptions:[iconProperty objectForKey:@"label"]]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + marker.icon = image; + + CGFloat anchorX = 0; + CGFloat anchorY = 0; + // The `anchor` property for the icon + if ([iconProperty valueForKey:@"anchor"] && [iconProperty valueForKey:@"anchor"] != [NSNull null]) { + NSArray *points = [iconProperty valueForKey:@"anchor"]; + anchorX = [[points objectAtIndex:0] floatValue] / image.size.width; + anchorY = [[points objectAtIndex:1] floatValue] / image.size.height; + marker.groundAnchor = CGPointMake(anchorX, anchorY); + } + + // The `infoWindowAnchor` property + if ([iconProperty valueForKey:@"infoWindowAnchor"] && [iconProperty valueForKey:@"infoWindowAnchor"] != [NSNull null]) { + NSArray *points = [iconProperty valueForKey:@"infoWindowAnchor"]; + anchorX = [[points objectAtIndex:0] floatValue] / image.size.width; + anchorY = [[points objectAtIndex:1] floatValue] / image.size.height; + marker.infoWindowAnchor = CGPointMake(anchorX, anchorY); + } + + + // The `visible` property + if (iconProperty[@"visible"] == [NSNumber numberWithBool:true]) { + marker.map = self.mapCtrl.map; + } else if (iconProperty[@"visible"] == [NSNumber numberWithBool:false]) { + marker.map = nil; + } + + if (animation) { + // Do animation, then send the result + [self setMarkerAnimation_:animation marker:marker callbackBlock:^(void) { + callbackBlock(YES, marker); + }]; + } else { + // Send the result + callbackBlock(YES, marker); + } + }); + + return; + } + + + + + if (self.mapCtrl.debuggable) { + NSLog(@"---- Load the icon from over the internet"); + } + + // + // Load the icon from over the internet + // + + iconPath = [iconPath regReplace:@"\\/\\.\\/" replaceTxt:@"/" options:0]; + iconPath = [iconPath regReplace:@"\\/+" replaceTxt:@"/" options:0]; + iconPath = [iconPath stringByReplacingOccurrencesOfString:@":/" withString:@"://"]; + NSURL *url = [NSURL URLWithString:iconPath]; + + [self downloadImageWithURL:url completionBlock:^(BOOL succeeded, UIImage *image) { + + if (!succeeded) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"[fail] url = %@", url); + // The `visible` property + if (iconProperty[@"visible"] == [NSNumber numberWithBool:true]) { + marker.map = self.mapCtrl.map; + } else if (iconProperty[@"visible"] == [NSNumber numberWithBool:false]) { + marker.map = nil; + } + if ([[UIImageCache sharedInstance].iconCacheKeys objectForKey:iconCacheKey]) { + [[UIImageCache sharedInstance].iconCacheKeys removeObjectForKey:iconCacheKey]; + } + + callbackBlock(NO, [NSString stringWithFormat:@"Can not load image from '%@'.", url]); + }); + return; + } + + + if (self.mapCtrl.debuggable) { + NSLog(@"[success] url = %@", url); + } + + if (width && height) { + image = [image resize:width height:height]; + } + + // Cache the icon image + NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData]; + [[UIImageCache sharedInstance] cacheImage:image forKey:iconCacheKey]; + [self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey]; + [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:1] forKey:iconCacheKey];; + //NSLog(@"--->confirm: key: %@, iconCacheKey : %@", iconKey, [self.mapCtrl.objects objectForKey:iconKey]); + + // Draw label + if ([iconProperty objectForKey:@"label"]) { + image = [self drawLabel:image labelOptions:[iconProperty objectForKey:@"label"]]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + marker.icon = image; + + // The `anchor` property for the icon + if ([iconProperty valueForKey:@"anchor"] && [iconProperty valueForKey:@"anchor"] != [NSNull null]) { + NSArray *points = [iconProperty valueForKey:@"anchor"]; + CGFloat anchorX = [[points objectAtIndex:0] floatValue] / image.size.width; + CGFloat anchorY = [[points objectAtIndex:1] floatValue] / image.size.height; + marker.groundAnchor = CGPointMake(anchorX, anchorY); + } + + + // The `infoWindowAnchor` property + if ([iconProperty valueForKey:@"infoWindowAnchor"] && [iconProperty valueForKey:@"infoWindowAnchor"] != [NSNull null]) { + NSArray *points = [iconProperty valueForKey:@"infoWindowAnchor"]; + CGFloat anchorX = [[points objectAtIndex:0] floatValue] / image.size.width; + CGFloat anchorY = [[points objectAtIndex:1] floatValue] / image.size.height; + marker.infoWindowAnchor = CGPointMake(anchorX, anchorY); + } + + // The `visible` property + if (iconProperty[@"visible"] == [NSNumber numberWithBool:true]) { + marker.map = self.mapCtrl.map; + } else if (iconProperty[@"visible"] == [NSNumber numberWithBool:false]) { + marker.map = nil; + } + + + if (animation) { + // Do animation, then send the result + [self setMarkerAnimation_:animation marker:marker callbackBlock:^(void) { + callbackBlock(YES, marker); + }]; + } else { + // Send the result + callbackBlock(YES, marker); + } + + }); + + + }]; + +} + +- (UIImage *)drawLabel:(UIImage *) image labelOptions:(NSDictionary *)labelOptions { + NSString *text = [NSString stringWithFormat:@"%@", [labelOptions objectForKey:@"text"]]; + if (text == nil) { + return image; + } + + UIFont *font; + float fontSize = 10; + BOOL isBold = NO; + BOOL isItalic = NO; + UIColor *textColor = UIColor.blackColor; + + if ([labelOptions objectForKey:@"fontSize"]) { + fontSize = [[labelOptions objectForKey:@"fontSize"] floatValue]; + } + if ([labelOptions objectForKey:@"bold"]) { + isBold = [[labelOptions objectForKey:@"bold"] boolValue]; + } + if ([labelOptions objectForKey:@"italic"]) { + isItalic = [[labelOptions objectForKey:@"italic"] boolValue]; + } + + if (isBold == YES && isItalic == YES) { + // ref: http://stackoverflow.com/questions/4713236/how-do-i-set-bold-and-italic-on-uilabel-of-iphone-ipad#21777132 + font = [UIFont systemFontOfSize:17.0f]; + UIFontDescriptor *fontDescriptor = [font.fontDescriptor + fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic]; + font = [UIFont fontWithDescriptor:fontDescriptor size:fontSize]; + } else if (isBold == TRUE && isItalic == FALSE) { + font = [UIFont boldSystemFontOfSize:fontSize]; + } else if (isBold == TRUE && isItalic == FALSE) { + font = [UIFont italicSystemFontOfSize:fontSize]; + } else { + font = [UIFont systemFontOfSize:fontSize]; + } + + + if ([labelOptions objectForKey:@"color"]) { + textColor = [[labelOptions objectForKey:@"color"] parsePluginColor]; + } + + // Calculate the size for the title strings + CGRect rect = [text + boundingRectWithSize: CGSizeMake(image.size.width, image.size.height) + options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) + attributes:[NSDictionary + dictionaryWithObject:font + forKey:NSFontAttributeName] + context:nil]; + + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + style.lineBreakMode = NSLineBreakByWordWrapping; + style.alignment = NSTextAlignmentCenter; + + + NSDictionary *attributes = @{ + NSForegroundColorAttributeName : textColor, + NSFontAttributeName : font, + NSParagraphStyleAttributeName : style + }; + + UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0); + [image drawInRect:CGRectMake(0,0, image.size.width, image.size.height)]; + [text drawInRect:CGRectMake((image.size.width - rect.size.width) /2 , (image.size.height - rect.size.height) /2, rect.size.width, rect.size.height) withAttributes:attributes]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + /* + //(debug) + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.5); + CGContextFillRect(context, rect); + */ + UIGraphicsEndImageContext(); + image = nil; + + + return newImage; +} + +- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *urlStr = url.absoluteString; + // Since ionic local server declines HTTP access for some reason, + // replace URL with file path + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *wwwPath = [mainBundle pathForResource:@"www/cordova" ofType:@"js"]; + wwwPath = [wwwPath stringByReplacingOccurrencesOfString:@"/cordova.js" withString:@""]; + if ([urlStr containsString:@"assets/"]) { + urlStr = [urlStr regReplace:@"^.*assets" replaceTxt:[NSString stringWithFormat:@"%@/assets/", wwwPath] options:NSRegularExpressionCaseInsensitive]; + } + // urlStr = [urlStr stringByReplacingOccurrencesOfString:wwwPath withString: @""]; + + // ionic 4 + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"http://localhost:8080" withString: wwwPath]; + + // ionic 5 + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"ionic://localhost" withString: wwwPath]; + + + if ([urlStr hasPrefix:@"file:"] || [urlStr hasPrefix:@"/"]) { + NSString *iconPath = [urlStr stringByReplacingOccurrencesOfString:@"file:" withString:@""]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:iconPath]) { + NSLog(@"(error)There is no file at '%@'.", iconPath); + completionBlock(NO, nil); + return; + } else { + UIImage *image = [UIImage imageNamed:iconPath]; + completionBlock(YES, image); + } + } + + NSURLRequest *req = [NSURLRequest requestWithURL:url + cachePolicy:NSURLRequestReturnCacheDataElseLoad + timeoutInterval:5]; + NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:req]; + if (cachedResponse != nil) { + UIImage *image = [[UIImage alloc] initWithData:cachedResponse.data]; + if (image) { + completionBlock(YES, image); + return; + } + } + + NSString *uniqueKey = url.absoluteString; + UIImage *image = [[UIImageCache sharedInstance] getCachedImageForKey:uniqueKey]; + if (image != nil) { + completionBlock(YES, image); + return; + } + + + //------------------------------------------------------------- + // Use NSURLSessionDataTask instead of [NSURLConnection sendAsynchronousRequest] + // https://stackoverflow.com/a/20871647 + //------------------------------------------------------------- + NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; + NSURLSessionDataTask *getTask = [session dataTaskWithRequest:req + completionHandler:^(NSData *data, NSURLResponse *res, NSError *error) { + [session finishTasksAndInvalidate]; + + UIImage *image = [UIImage imageWithData:data]; + if (image) { + [[UIImageCache sharedInstance] cacheImage:image forKey:uniqueKey]; + completionBlock(YES, image); + return; + } + + completionBlock(NO, nil); + + }]; + [getTask resume]; + //------------------------------------------------------------- + // NSURLConnection sendAsynchronousRequest is deprecated. + //------------------------------------------------------------- + /* + [NSURLConnection sendAsynchronousRequest:req + queue:self.mapCtrl.executeQueue + completionHandler:^(NSURLResponse *res, NSData *data, NSError *error) { + if ( !error ) { + [self.icons setObject:data forKey:uniqueKey cost:data.length]; + UIImage *image = [UIImage imageWithData:data]; + completionBlock(YES, image); + } else { + completionBlock(NO, nil); + } + + }]; + */ + + + }]; +} +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarkerCluster.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarkerCluster.h new file mode 100644 index 0000000..5f943af --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarkerCluster.h @@ -0,0 +1,27 @@ +// +// PluginMarkerCluster.h +// cordova-googlemaps-plugin +// +// Created by masashi. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" +#import "PluginUtil.h" +#import "PluginMarker.h" + +@interface PluginMarkerCluster : PluginMarker +@property (atomic, strong) NSMutableDictionary *debugFlags; +@property (atomic, strong) NSMutableDictionary *pluginMarkers; +@property (atomic, strong) NSMutableDictionary *waitCntManager; +@property (atomic, strong) NSMutableDictionary *allResults; +@property (atomic, strong) NSMutableArray *deleteMarkers; +@property (atomic, strong) dispatch_semaphore_t semaphore; +@property (atomic, strong) dispatch_semaphore_t deleteThreadLock; +@property (nonatomic) BOOL stopFlag; + +- (void)create:(CDVInvokedUrlCommand*)command; +- (void)redrawClusters:(CDVInvokedUrlCommand*)command; +- (void)remove:(CDVInvokedUrlCommand*)command; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarkerCluster.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarkerCluster.m new file mode 100644 index 0000000..cbf706f --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginMarkerCluster.m @@ -0,0 +1,618 @@ +// +// PluginMarkerCluster.m +// cordova-googlemaps-plugin +// +// Created by masashi. +// +// + +#import "PluginMarkerCluster.h" +@implementation PluginMarkerCluster + +const NSString *GEOCELL_ALPHABET = @"0123456789abcdef"; +const int GEOCELL_GRID_SIZE = 4; + + +- (void)pluginInitialize +{ + if (self.initialized) { + return; + } + self.initialized = YES; + [super pluginInitialize]; + + // Initialize this plugin + self.waitCntManager = [NSMutableDictionary dictionary]; + self.allResults = [NSMutableDictionary dictionary]; + self.pluginMarkers = [NSMutableDictionary dictionary]; + self.debugFlags = [NSMutableDictionary dictionary]; + self.deleteMarkers = [NSMutableArray array]; + self.semaphore = dispatch_semaphore_create(0); + self.deleteThreadLock = dispatch_semaphore_create(0); + self.stopFlag = NO; + int waitTimeOut = 1000 * 1000 * 1000; // 1sec + + //--------------------- + // Delete thread + //--------------------- + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + while(!self.stopFlag) { + @synchronized (self.deleteThreadLock) { + dispatch_semaphore_wait(self.deleteThreadLock, dispatch_time(DISPATCH_TIME_NOW, waitTimeOut)); + } + if ([self.deleteMarkers count] == 0) { + continue; + } + + dispatch_async(dispatch_get_main_queue(), ^(void) { + @synchronized (self.deleteMarkers) { + NSString *markerId; + GMSMarker *marker = nil; + //--------- + // delete + //--------- + int deleteCnt = (int)[self.deleteMarkers count]; + for (int i = (deleteCnt - 1); i > -1; i--) { + markerId = [self.deleteMarkers objectAtIndex:i]; + + @synchronized (self.mapCtrl.objects) { + marker = [self.mapCtrl.objects objectForKey: markerId]; + } + + @synchronized (self.pluginMarkers) { + if ([[self.pluginMarkers objectForKey:markerId] isEqualToString:@"WORKING"]) { + [self.pluginMarkers setObject:@"DELETED" forKey:markerId]; + } else { + @synchronized (self.mapCtrl.objects) { + [self _removeMarker:marker]; + marker = nil; + [self.mapCtrl.objects removeObjectForKey:markerId]; + if ([self.mapCtrl.objects objectForKey:[NSString stringWithFormat:@"marker_property_%@", markerId]]) { + [self.mapCtrl.objects removeObjectForKey:[NSString stringWithFormat:@"marker_property_%@", markerId]]; + } + + if ([self.mapCtrl.objects objectForKey:[NSString stringWithFormat:@"marker_icon_%@", markerId]]) { + [self.mapCtrl.objects removeObjectForKey:[NSString stringWithFormat:@"marker_icon_%@", markerId]]; + } + } + [self.pluginMarkers removeObjectForKey:markerId]; + [self.deleteMarkers removeObjectAtIndex:i]; + } + } + } + } + + }); + + } + }); +} + +- (void)pluginUnload +{ + self.stopFlag = true; + [super pluginUnload]; + + @synchronized (self.pluginMarkers) { + NSString *key; + NSArray *keys = self.pluginMarkers.allKeys; + for (int i = 0; i < keys.count; i++) { + key = [keys objectAtIndex:i]; + [self.pluginMarkers setObject:@"DELETED" forKey:key]; + [self.deleteMarkers addObject:key]; + } + } + +} + +- (void)remove:(CDVInvokedUrlCommand*)command { + + NSString *clusterId = [command.arguments objectAtIndex: 0]; + @synchronized (self.debugFlags) { + [self.debugFlags removeObjectForKey:clusterId]; + [self.waitCntManager removeObjectForKey:clusterId]; + } + + @synchronized (self.pluginMarkers) { + NSString *key; + NSArray *keys = self.pluginMarkers.allKeys; + for (int i = 0; i < keys.count; i++) { + key = [keys objectAtIndex:i]; + if ([key hasPrefix:clusterId]) { + [self.pluginMarkers setObject:@"DELETED" forKey:key]; + [self.deleteMarkers addObject:key]; + } + } + } + + dispatch_semaphore_signal(self.deleteThreadLock); + + + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK ]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + +} +- (void)create:(CDVInvokedUrlCommand*)command { + NSDictionary *params = [command.arguments objectAtIndex:1]; + NSArray *positionList = [params objectForKey:@"positionList"]; + NSMutableArray *geocellList = [NSMutableArray array]; + NSMutableDictionary *position; + double lat, lng; + + for (int i = 0; i < [positionList count]; i++) { + position = [positionList objectAtIndex:i]; + lat = [[position objectForKey:@"lat"] doubleValue]; + lng = [[position objectForKey:@"lng"] doubleValue]; + [geocellList addObject:[self getGeocell:lat lng:lng resolution:12]]; + } + + NSString *clusterId = [NSString stringWithFormat:@"markercluster_%lu%d", command.hash, arc4random() % 100000]; + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + [result setObject:geocellList forKey:@"geocellList"]; + [result setObject:clusterId forKey:@"__pgmId"]; + [result setObject:[NSNumber numberWithFloat:[[UIScreen mainScreen] scale]] forKey:@"scale"]; + + [self.debugFlags setObject:[NSNumber numberWithBool:[[params objectForKey:@"debug"] boolValue]] forKey:clusterId]; + + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)redrawClusters:(CDVInvokedUrlCommand*)command { + + __block NSMutableArray *updateClusterIDs = [NSMutableArray array]; + __block NSMutableDictionary *changeProperties = [NSMutableDictionary dictionary]; + __block NSString *clusterId = [command.arguments objectAtIndex: 0]; + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + [self.allResults removeAllObjects]; + BOOL isDebug = [[self.debugFlags objectForKey:clusterId] boolValue]; + + __block NSDictionary *params = [command.arguments objectAtIndex:1]; + NSString *clusterId_markerId, *markerId; + + NSMutableArray *new_or_update = nil; + if ([params objectForKey:@"new_or_update"]) { + new_or_update = [params objectForKey:@"new_or_update"]; + } + + //--------------------------- + // Determine new or update + //--------------------------- + int new_or_updateCnt = 0; + if (new_or_update != nil) { + new_or_updateCnt = (int)[new_or_update count]; + } + + NSDictionary *clusterData; + NSMutableDictionary *properties; + for (int i = 0; i < new_or_updateCnt; i++) { + clusterData = [new_or_update objectAtIndex:i]; + markerId = [clusterData objectForKey:@"__pgmId"]; + clusterId_markerId = [NSString stringWithFormat:@"%@-%@", clusterId, markerId]; + + // Save the marker properties + [self.mapCtrl.objects setObject:clusterData forKey:[NSString stringWithFormat:@"marker_property_%@", clusterId_markerId]]; + + // Set the WORKING status flag + [updateClusterIDs addObject:clusterId_markerId]; + @synchronized (self.pluginMarkers) { + [self.pluginMarkers setObject:@"WORKING" forKey:clusterId_markerId]; + } + + // Prepare the marker properties for addMarker() + properties = [NSMutableDictionary dictionary]; + [properties setObject:[clusterData objectForKey:@"position"] forKey:@"position"]; + if ([clusterData objectForKey:@"title"]) { + [properties setObject:[clusterData objectForKey:@"title"] forKey:@"title"]; + } + if (clusterData[@"visible"]) { + [properties setObject:[clusterData objectForKey:@"visible"] forKey:@"visible"]; + } else { + [properties setObject:[NSNumber numberWithBool:true] forKey:@"visible"]; + } + [properties setObject:clusterId_markerId forKey:@"__pgmId"]; + + if ([clusterData objectForKey:@"icon"]) { + id iconObj = [clusterData objectForKey:@"icon"]; + if ([[iconObj class] isSubclassOfClass:[NSString class]]) { + NSMutableDictionary *iconProperties = [NSMutableDictionary dictionary]; + [iconProperties setObject:iconObj forKey:@"url"]; + [properties setObject:iconProperties forKey:@"icon"]; + + } else if ([[iconObj class] isSubclassOfClass:[NSDictionary class]]) { + + NSMutableDictionary *iconProperties = [NSMutableDictionary dictionaryWithDictionary:iconObj]; + + if ([[clusterData objectForKey:@"isClusterIcon"] boolValue]) { + + if ([iconProperties objectForKey:@"label"]) { + + NSMutableDictionary *label = [NSMutableDictionary dictionaryWithDictionary:[iconProperties objectForKey:@"label"]]; + if (isDebug == YES) { + [label setObject:[clusterId_markerId stringByReplacingOccurrencesOfString:clusterId withString:@""] forKey:@"text"]; + } else { + [label setObject:[clusterData objectForKey:@"count"] forKey:@"text"]; + } + [iconProperties setObject:label forKey:@"label"]; + + } else { + NSMutableDictionary *label = [NSMutableDictionary dictionary]; + if (isDebug == YES) { + [label setObject:[clusterId_markerId stringByReplacingOccurrencesOfString:clusterId withString:@""] forKey:@"text"]; + } else { + [label setObject:[NSNumber numberWithInt:15] forKey:@"fontSize"]; + [label setObject:[NSNumber numberWithBool:TRUE] forKey:@"bold"]; + [label setObject:[clusterData objectForKey:@"count"] forKey:@"text"]; + } + [iconProperties setObject:label forKey:@"label"]; + } + + + } + + if ([iconProperties objectForKey:@"anchor"]) { + [iconProperties setObject:[iconProperties objectForKey:@"anchor"] forKey:@"anchor"]; + } + if ([iconProperties objectForKey:@"infoWindowAnchor"]) { + [iconProperties setObject:[iconProperties objectForKey:@"infoWindowAnchor"] forKey:@"infoWindowAnchor"]; + } + [properties setObject:iconProperties forKey:@"icon"]; + } + } // if ([clusterData objectForKey:@"icon"]) {..} + + [changeProperties setObject:properties forKey:clusterId_markerId]; + + } // for (int i = 0; i < new_or_updateCnt; i++) { .. } + + + if ([updateClusterIDs count] == 0) { + [self deleteProcess:params clusterId:clusterId]; + [self endRedraw:command]; + return; + } + + //--------------------------- + // mapping markers on the map + //--------------------------- + [[NSOperationQueue mainQueue] addOperationWithBlock: ^{ + //self.mapCtrl.map.selectedMarker = nil; + NSString *clusterId_markerId; + NSMutableDictionary *markerProperties; + GMSMarker *marker; + CLLocationCoordinate2D position; + double latitude, longitude; + BOOL isNew; + NSDictionary *positionJSON; + + //--------------------- + // new or update + //--------------------- + [self.waitCntManager setObject:[NSNumber numberWithInteger:[updateClusterIDs count]] forKey:clusterId]; + for (int i = 0; i < [updateClusterIDs count]; i++) { + clusterId_markerId = [updateClusterIDs objectAtIndex:i]; + @synchronized(self.pluginMarkers) { + [self.pluginMarkers setObject:@"WORKING" forKey:clusterId_markerId]; + } + + // Get the marker properties + markerProperties = [self.mapCtrl.objects objectForKey:[NSString stringWithFormat:@"marker_property_%@", clusterId_markerId]]; + + isNew = [self.mapCtrl.objects objectForKey:clusterId_markerId] == nil; + //-------------------------- + // regular marker + //-------------------------- + if ([clusterId_markerId containsString:@"-marker_"]) { + if (isNew) { + markerProperties = [self.mapCtrl.objects objectForKey:[NSString stringWithFormat:@"marker_property_%@", clusterId_markerId]]; + [super _create:clusterId_markerId markerOptions:markerProperties callbackBlock:^(BOOL successed, id resultObj) { + + @synchronized (self.pluginMarkers) { + if (successed) { + //((GMSMarker *)resultObj).map = self.mapCtrl.map; + [self.pluginMarkers setObject:@"CREATED" forKey:clusterId_markerId]; + + NSArray *tmp = [clusterId_markerId componentsSeparatedByString:@"-"]; + NSString *markerId = [tmp objectAtIndex:1]; + NSMutableDictionary *createResult = [NSMutableDictionary dictionary]; + GMSMarker *marker = resultObj; + UIImage *image; + NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData]; + // retrieve key mapping set by the PluginMarker.setIcon_ + NSString *iconCacheKey = [self.mapCtrl.objects objectForKey:iconKey]; + if (iconCacheKey != nil) { + // use it to retrieve cached icon + image = [[UIImageCache sharedInstance] getCachedImageForKey:iconCacheKey]; + } + if (image == nil) { + // fallback to old behaviour + image = [[UIImageCache sharedInstance] getCachedImageForKey:iconKey]; + } + if (image != nil) { + [createResult setObject:[NSNumber numberWithInt: (int)image.size.width] forKey:@"width"]; + [createResult setObject:[NSNumber numberWithInt: (int)image.size.height] forKey:@"height"]; + } else { + [createResult setObject:[NSNumber numberWithInt: 24] forKey:@"width"]; + [createResult setObject:[NSNumber numberWithInt: 40] forKey:@"height"]; + } + [self.allResults setObject:createResult forKey:markerId]; + } else { + //-------------------------------------- + // Could not read icon for some reason + //-------------------------------------- + [self.pluginMarkers setObject:@"DELETED" forKey:clusterId_markerId]; + NSLog(@"(error) %@", resultObj); + @synchronized (self.deleteMarkers) { + [self.deleteMarkers addObject:clusterId_markerId]; + } + } + } + [self decreaseWaitWithClusterId:clusterId command:command]; + + }]; + } else { + + marker = [self.mapCtrl.objects objectForKey:clusterId_markerId]; + //---------------------------------------- + // Set the title and snippet properties + //---------------------------------------- + if ([markerProperties objectForKey:@"title"]) { + marker.title = [markerProperties objectForKey:@"title"]; + } + if ([markerProperties objectForKey:@"snippet"]) { + marker.snippet = [markerProperties objectForKey:@"snippet"]; + } + @synchronized (self.pluginMarkers) { + [self.pluginMarkers setObject:@"CREATED" forKey:clusterId_markerId]; + } + [self decreaseWaitWithClusterId:clusterId command:command]; + } + continue; + } + //-------------------------- + // cluster icon + //-------------------------- + markerProperties = [changeProperties objectForKey:clusterId_markerId]; + + + if (isNew) { + // If the requested id is new location, create a marker + positionJSON = [markerProperties objectForKey:@"position"]; + latitude = [[positionJSON objectForKey:@"lat"] doubleValue]; + longitude = [[positionJSON objectForKey:@"lng"] doubleValue]; + position = CLLocationCoordinate2DMake(latitude, longitude); + marker = [GMSMarker markerWithPosition:position]; + marker.userData = clusterId_markerId; + + // Store the marker instance with markerId + @synchronized (self.mapCtrl.objects) { + [self.mapCtrl.objects setObject:marker forKey:clusterId_markerId]; + } + } else { + @synchronized (self.mapCtrl.objects) { + marker = [self.mapCtrl.objects objectForKey:clusterId_markerId]; + } + } + + //---------------------------------------- + // Set the title and snippet properties + //---------------------------------------- + if ([markerProperties objectForKey:@"title"]) { + marker.title = [markerProperties objectForKey:@"title"]; + } + if ([markerProperties objectForKey:@"snippet"]) { + marker.snippet = [markerProperties objectForKey:@"snippet"]; + } + + + if ([markerProperties objectForKey:@"icon"]) { + PluginMarkerCluster *self_ = self; + NSDictionary *icon = [markerProperties objectForKey:@"icon"]; + [self setIconToClusterMarker:clusterId_markerId marker:marker iconProperty:icon callbackBlock:^(BOOL successed, id resultObj) { + if (successed == NO) { + //-------------------------------------- + // Could not read icon for some reason + //-------------------------------------- + NSLog(@"(error) %@", resultObj); + @synchronized (self_.deleteMarkers) { + [self_.deleteMarkers addObject:clusterId_markerId]; + } + @synchronized (self_.pluginMarkers) { + [self_.pluginMarkers setObject:@"DELETED" forKey:clusterId_markerId]; + } + } else { + //-------------------------------------- + // Marker was updated + //-------------------------------------- + marker.map = self_.mapCtrl.map; + @synchronized (self_.pluginMarkers) { + [self_.pluginMarkers setObject:@"CREATED" forKey:clusterId_markerId]; + } + } + [self_ decreaseWaitWithClusterId:clusterId command:command]; + }]; + } else { + marker.map = self.mapCtrl.map; + @synchronized (self.pluginMarkers) { + [self.pluginMarkers setObject:@"CREATED" forKey:clusterId_markerId]; + } + [self decreaseWaitWithClusterId:clusterId command:command]; + } + + } // for (int i = 0; i < [updateClusterIDs count]; i++) {..} + + [self deleteProcess:params clusterId:clusterId]; + + }]; // [[NSOperationQueue mainQueue] addOperationWithBlock: ^{..} + + + }]; // dispatch_async +} + +- (void) endRedraw:(CDVInvokedUrlCommand*)command { + NSLog(@"--->allResults = %@", self.allResults); + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:self.allResults]; + [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void) deleteProcess:(NSDictionary *) params clusterId:(NSString *)clusterId{ + + NSMutableArray *deleteClusters = nil; + if ([params objectForKey:@"delete"]) { + deleteClusters = [params objectForKey:@"delete"]; + } + if (deleteClusters != nil) { + //------------------------------------- + // delete markers on the delete thread + //------------------------------------- + int deleteCnt = 0; + deleteCnt = (int)[deleteClusters count]; + NSString *clusterId_markerId; + @synchronized (self.deleteMarkers) { + for (int i = 0; i < deleteCnt; i++) { + clusterId_markerId = [NSString stringWithFormat:@"%@-%@", + clusterId, [deleteClusters objectAtIndex:i]]; + [self.deleteMarkers addObject:clusterId_markerId]; + } + } + + dispatch_semaphore_signal(self.deleteThreadLock); + } + + +} + +- (void) setIconToClusterMarker:(NSString *) markerId marker:(GMSMarker *)marker iconProperty:(NSDictionary *)iconProperty callbackBlock:(void (^)(BOOL successed, id resultObj)) callbackBlock { + PluginMarkerCluster *self_ = self; + @synchronized (_pluginMarkers) { + if ([[_pluginMarkers objectForKey:markerId] isEqualToString:@"DELETED"]) { + [self _removeMarker:marker]; + if ([self.pluginMarkers objectForKey:markerId]) { + [_pluginMarkers removeObjectForKey:markerId]; + } + + callbackBlock(NO, @"marker has been removed"); + return; + } + [_pluginMarkers setObject:@"WORKING" forKey:markerId]; + } + [self setIcon_:marker iconProperty:iconProperty callbackBlock:^(BOOL successed, id resultObj) { + if (successed) { + //---------------------------------------------------------------------- + // If marker has been already marked as DELETED, remove the marker. + //---------------------------------------------------------------------- + GMSMarker *marker = resultObj; + @synchronized (self_.pluginMarkers) { + if ([[self_.pluginMarkers objectForKey:markerId] isEqualToString:@"DELETED"]) { + [self_ _removeMarker:marker]; + [self_.pluginMarkers removeObjectForKey:markerId]; + callbackBlock(YES, nil); + return; + } + + [self_.pluginMarkers setObject:@"CREATED" forKey:markerId]; + callbackBlock(YES, marker); + return; + } + } else { + if (marker != nil && marker.userData != nil) { + [self_ _removeMarker:marker]; + } + @synchronized (self_.pluginMarkers) { + [self_.pluginMarkers removeObjectForKey:markerId]; + } + + callbackBlock(NO, resultObj); + } + }]; +} + +- (void) decreaseWaitWithClusterId:(NSString *) clusterId command:(CDVInvokedUrlCommand*)command { + + @synchronized (_waitCntManager) { + int waitCnt = [[_waitCntManager objectForKey:clusterId] intValue]; + waitCnt = waitCnt - 1; + [self.waitCntManager setObject:[NSNumber numberWithInt:waitCnt] forKey:clusterId]; + if (waitCnt == 0) { + [self endRedraw: command]; + } + } +} + +- (NSString *)getGeocell:(double) lat lng:(double) lng resolution:(int)resolution { + + NSMutableString *cell = [NSMutableString string]; + double north = 90.0; + double south = -90.0; + double east = 180.0; + double west = -180.0; + double subcell_lng_span, subcell_lat_span; + char x, y; + + while ([cell length] < resolution) { + subcell_lng_span = (east - west) / GEOCELL_GRID_SIZE; + subcell_lat_span = (north - south) / GEOCELL_GRID_SIZE; + + x = (char)MIN(floor(GEOCELL_GRID_SIZE * (lng - west) / (east - west)), GEOCELL_GRID_SIZE - 1); + y = (char)MIN(floor(GEOCELL_GRID_SIZE * (lat - south) / (north - south)), GEOCELL_GRID_SIZE - 1); + [cell appendString:[NSString stringWithFormat:@"%c", [self _subdiv_char:x y:y]]]; + + south += subcell_lat_span * y; + north = south + subcell_lat_span; + + west += subcell_lng_span * x; + east = west + subcell_lng_span; + } + + return cell; +} + +- (char) _subdiv_char:(int) posX y:(int)posY { + return [GEOCELL_ALPHABET characterAtIndex:( + (posY & 2) << 2 | + (posX & 2) << 1 | + (posY & 1) << 1 | + (posX & 1))]; +} + + +- (GMSCoordinateBounds *)computeBox:(NSString *) geocell { + NSString *geoChar; + double north = 90.0; + double south = -90.0; + double east = 180.0; + double west = -180.0; + + double subcell_lng_span, subcell_lat_span; + int x, y, pos; + NSRange range; + + for (int i = 0; i < [geocell length]; i++) { + geoChar = [geocell substringWithRange:NSMakeRange(i, 1)]; + range = [GEOCELL_ALPHABET rangeOfString:geoChar]; + pos = (int)range.location; + + subcell_lng_span = (east - west) / GEOCELL_GRID_SIZE; + subcell_lat_span = (north - south) / GEOCELL_GRID_SIZE; + + x = (int) ((int)floor(pos / 4) % 2 * 2 + pos % 2); + y = (int) (pos - floor(pos / 4) * 4); + y = y >> 1; + y += floor(pos / 4) > 1 ? 2 : 0; + + south += subcell_lat_span * y; + north = south + subcell_lat_span; + + west += subcell_lng_span * x; + east = west + subcell_lng_span; + } + + GMSMutablePath *mutablePath = [[GMSMutablePath alloc] init]; + [mutablePath addCoordinate:CLLocationCoordinate2DMake(south, west)]; + [mutablePath addCoordinate:CLLocationCoordinate2DMake(north, east)]; + + return [[GMSCoordinateBounds alloc] initWithPath:mutablePath]; +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginObjects.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginObjects.h new file mode 100644 index 0000000..f37d977 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginObjects.h @@ -0,0 +1,35 @@ +// +// PluginObjects +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// +#ifndef PluginObjects_h +#define PluginObjects_h + +#import + +@interface PluginObjects : NSObject + +@property (atomic, strong) NSMutableDictionary *objects; + +// set +- (void)setObject:(id)objId forKey:(NSString*)key; + +// get +- (id)objectForKey:(NSString*)key; + +// remove +- (void)removeObjectForKey:(NSString*)key; + +// remove all +- (void)removeAllObjects; + +// return all keys +- (NSArray*)allKeys; + +@end + + +#endif diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginObjects.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginObjects.m new file mode 100644 index 0000000..9119ffb --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginObjects.m @@ -0,0 +1,55 @@ +// +// PluginObjects +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// +#import "PluginObjects.h" + + +@implementation PluginObjects + +static NSObject *_lock; + +- (instancetype)init { + self = [super init]; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _lock = [NSObject alloc]; + }); + self.objects = [NSMutableDictionary dictionary]; + return self; +} + +- (void)setObject:(id)objId forKey:(NSString*)key { + @synchronized(_lock) { + [self.objects setObject:objId forKey:key]; + } +} + +- (id)objectForKey:(NSString*)key { + @synchronized(_lock) { + return [self.objects objectForKey:key]; + } +} +- (void)removeObjectForKey:(NSString*)key { + @synchronized(_lock) { + [self.objects removeObjectForKey:key]; + } +} + +- (void)removeAllObjects { + @synchronized(_lock) { + [self.objects removeAllObjects]; + } +} + +- (NSArray*)allKeys { + @synchronized(_lock) { + return [self.objects allKeys]; + } +} + + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolygon.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolygon.h new file mode 100644 index 0000000..bfa5836 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolygon.h @@ -0,0 +1,32 @@ +// +// PluginPolygon.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" +@interface PluginPolygon : CDVPlugin +@property (nonatomic, strong) PluginMapViewController* mapCtrl; +@property (nonatomic) BOOL initialized; + +- (void)create:(CDVInvokedUrlCommand*)command; +- (void)setFillColor:(CDVInvokedUrlCommand*)command; +- (void)setStrokeColor:(CDVInvokedUrlCommand*)command; +- (void)setStrokeWidth:(CDVInvokedUrlCommand*)command; +- (void)removePointAt:(CDVInvokedUrlCommand*)command; +- (void)setPointAt:(CDVInvokedUrlCommand*)command; +- (void)setPoints:(CDVInvokedUrlCommand*)command; +- (void)insertPointAt:(CDVInvokedUrlCommand*)command; +- (void)setPointOfHoleAt:(CDVInvokedUrlCommand*)command; +- (void)removePointOfHoleAt:(CDVInvokedUrlCommand*)command; +- (void)insertPointOfHoleAt:(CDVInvokedUrlCommand*)command; +- (void)setHoles:(CDVInvokedUrlCommand*)command; +- (void)setZIndex:(CDVInvokedUrlCommand*)command; +- (void)setClickable:(CDVInvokedUrlCommand*)command; +- (void)setVisible:(CDVInvokedUrlCommand*)command; +- (void)remove:(CDVInvokedUrlCommand*)command; +- (void)setGeodesic:(CDVInvokedUrlCommand*)command; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolygon.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolygon.m new file mode 100644 index 0000000..ac532a6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolygon.m @@ -0,0 +1,746 @@ +// +// PluginPolygon.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginPolygon.h" + +@implementation PluginPolygon + +-(void)setPluginViewController:(PluginViewController *)viewCtrl +{ + self.mapCtrl = (PluginMapViewController *)viewCtrl; +} + +- (void)pluginUnload +{ + // Plugin destroy + NSArray *keys = [self.mapCtrl.objects allKeys]; + NSString *key; + for (int i = 0; i < [keys count]; i++) { + key = [keys objectAtIndex:i]; + if ([key hasPrefix:@"polygon_property"]) { + key = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:key]; + polygon.map = nil; + polygon = nil; + } + [self.mapCtrl.objects removeObjectForKey:key]; + } + + key = nil; + keys = nil; + + NSString *pluginId = [NSString stringWithFormat:@"%@-polygon", self.mapCtrl.overlayId]; + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + [cdvViewController.pluginObjects removeObjectForKey:pluginId]; + [cdvViewController.pluginsMap setValue:nil forKey:pluginId]; + pluginId = nil; +} + + +- (void)pluginInitialize +{ + if (self.initialized) { + return; + } + self.initialized = YES; + [super pluginInitialize]; +} + +-(void)create:(CDVInvokedUrlCommand *)command +{ + + // Parse the polygonOptions + NSDictionary *json = [command.arguments objectAtIndex:1]; + NSString *idBase = [command.arguments objectAtIndex:2]; + + GMSMutablePath *mutablePath = [GMSMutablePath path]; + NSArray *points = [json objectForKey:@"points"]; + int i = 0; + NSDictionary *latLng; + for (i = 0; i < points.count; i++) { + latLng = [points objectAtIndex:i]; + [mutablePath addCoordinate:CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue])]; + } + + // Create paths of the hole property if specified. + NSMutableArray *holePaths = [NSMutableArray array]; + if ([json valueForKey:@"holes"] && [json valueForKey:@"holes"] != [NSNull null]) { + NSArray *holes = [json objectForKey:@"holes"]; + NSArray *latLngArray; + NSDictionary *latLng; + int j; + + for (i = 0; i < holes.count; i++) { + latLngArray = [holes objectAtIndex:i]; + GMSMutablePath *holePath = [GMSMutablePath path]; + for (j = 0; j < latLngArray.count; j++) { + latLng = [latLngArray objectAtIndex:j]; + [holePath addLatitude:[[latLng objectForKey:@"lat"] doubleValue] longitude:[[latLng objectForKey:@"lng"] doubleValue]]; + } + [holePaths addObject:holePath]; + } + } + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + // Create the polygon, and assign it to the map on UI thread. + GMSPolygon *polygon = [GMSPolygon polygonWithPath:mutablePath]; + + if (holePaths != nil) { + polygon.holes = holePaths; + } + + BOOL isVisible = YES; + + // Visible property + NSString *visibleValue = [NSString stringWithFormat:@"%@", json[@"visible"]]; + if ([@"0" isEqualToString:visibleValue]) { + // false + isVisible = NO; + polygon.map = nil; + } else { + // true or default + polygon.map = self.mapCtrl.map; + } + + BOOL isClickable = NO; + if ([[json valueForKey:@"clickable"] boolValue]) { + isClickable = YES; + } + if ([[json valueForKey:@"geodesic"] boolValue]) { + polygon.geodesic = YES; + } + NSArray *rgbColor = [json valueForKey:@"fillColor"]; + polygon.fillColor = [rgbColor parsePluginColor]; + + rgbColor = [json valueForKey:@"strokeColor"]; + polygon.strokeColor = [rgbColor parsePluginColor]; + + polygon.strokeWidth = [[json valueForKey:@"strokeWidth"] floatValue]; + polygon.zIndex = [[json valueForKey:@"zIndex"] floatValue]; + + // Since this plugin provide own click detection, + // disable default clickable feature. + polygon.tappable = NO; + + // Register polygon to the overlayManager. + NSString *id = [NSString stringWithFormat:@"polygon_%@", idBase]; + [self.mapCtrl.objects setObject:polygon forKey: id]; + polygon.title = id; + + // Run the below code on background thread. + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + //--------------------------- + // Keep the properties + //--------------------------- + NSString *propertyId = [NSString stringWithFormat:@"polygon_property_%@", idBase]; + + // points + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + [properties setObject:mutablePath forKey:@"mutablePath"]; + // holes + [properties setObject:holePaths forKey:@"holePaths"]; + // bounds (pre-calculate for click detection) + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + // isVisible + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"isVisible"]; + // isClickable + [properties setObject:[NSNumber numberWithBool:isClickable] forKey:@"isClickable"]; + // geodesic + [properties setObject:[NSNumber numberWithBool:polygon.geodesic] forKey:@"geodesic"]; + // zIndex + [properties setObject:[NSNumber numberWithDouble:polygon.zIndex] forKey:@"zIndex"];; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + + //--------------------------- + // Result for JS + //--------------------------- + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + [result setObject:id forKey:@"__pgmId"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; + + +} + +-(void)insertHoleAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSInteger index = [[command.arguments objectAtIndex:1] integerValue]; + GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + NSArray *holes = [command.arguments objectAtIndex:2]; + + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + NSMutableArray *holePaths = (NSMutableArray *)[properties objectForKey:@"holePaths"]; + + + // Insert a hole into the holes property + NSArray *latLngArray; + NSDictionary *latLng; + int i; + + GMSMutablePath *path = [GMSMutablePath path]; + for (i = 0; i < [holes count]; i++) { + latLng = [latLngArray objectAtIndex:i]; + [path addLatitude:[[latLng objectForKey:@"lat"] doubleValue] longitude:[[latLng objectForKey:@"lng"] doubleValue]]; + } + [holePaths insertObject:path atIndex:index]; + + // Save the property + [properties setObject:holePaths forKey:@"holePaths"]; + + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + polygon.holes = holePaths; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; + +} + + + +-(void)removePointOfHoleAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSInteger holeIndex = [[command.arguments objectAtIndex:1] integerValue]; + NSInteger pointIndex = [[command.arguments objectAtIndex:2] integerValue]; + //GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + NSMutableArray *holePaths = (NSMutableArray *)[properties objectForKey:@"holePaths"]; + + // Insert a point into the specified hole + GMSMutablePath *path = [holePaths objectAtIndex:holeIndex]; + [path removeCoordinateAtIndex:pointIndex]; + [holePaths setObject:path atIndexedSubscript:holeIndex]; + + // Save the property + [properties setObject:holePaths forKey:@"holePaths"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +-(void)setPointOfHoleAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSInteger holeIndex = [[command.arguments objectAtIndex:1] integerValue]; + NSInteger pointIndex = [[command.arguments objectAtIndex:2] integerValue]; + GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + NSDictionary *latLng = [command.arguments objectAtIndex:3]; + + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + NSMutableArray *holePaths = (NSMutableArray *)[properties objectForKey:@"holePaths"]; + + // Insert a point into the specified hole + GMSMutablePath *path = [holePaths objectAtIndex:holeIndex]; + CLLocationCoordinate2D position = CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue]); + [path replaceCoordinateAtIndex:pointIndex withCoordinate:position]; + [holePaths setObject:path atIndexedSubscript:holeIndex]; + + // Save the property + [properties setObject:holePaths forKey:@"holePaths"]; + + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + polygon.holes = holePaths; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; +} + +-(void)setHoles:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSArray *holeList = [command.arguments objectAtIndex:1]; + GMSMutablePath *hole; + + GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + NSDictionary *latLng; + + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + // Remove current holes + NSMutableArray *holePaths = (NSMutableArray *)[properties objectForKey:@"holePaths"]; + for (int i = 0; i < holePaths.count; i++) { + hole = [holePaths objectAtIndex:i]; + [hole removeAllCoordinates]; + } + [holePaths removeAllObjects]; + + // update all holes + GMSMutablePath *path; + NSArray *holePositions; + for (int i = 0; i < holeList.count; i++) { + path = [GMSMutablePath path]; + holePositions = [holeList objectAtIndex:i]; + for (int j = 0; j < holePositions.count; j++) { + latLng = [holePositions objectAtIndex:j]; + [path addCoordinate:CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue])]; + } + [holePaths addObject:path]; + } + + // Save the property + [properties setObject:holePaths forKey:@"holePaths"]; + + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + polygon.holes = holePaths; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; +} + +-(void)insertPointOfHoleAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSInteger holeIndex = [[command.arguments objectAtIndex:1] integerValue]; + NSInteger pointIndex = [[command.arguments objectAtIndex:2] integerValue]; + GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + NSDictionary *latLng = [command.arguments objectAtIndex:3]; + + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + NSMutableArray *holePaths = (NSMutableArray *)[properties objectForKey:@"holePaths"]; + + // Insert a point into the specified hole + GMSMutablePath *path = [holePaths objectAtIndex:holeIndex]; + CLLocationCoordinate2D position = CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue]); + [path insertCoordinate:position atIndex:pointIndex]; + [holePaths setObject:path atIndexedSubscript:holeIndex]; + + // Save the property + [properties setObject:holePaths forKey:@"holePaths"]; + + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + polygon.holes = holePaths; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; +} + + + +-(void)removePointAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSInteger index = [[command.arguments objectAtIndex:1] integerValue]; + //GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + GMSMutablePath *mutablePath = (GMSMutablePath *)[properties objectForKey:@"mutablePath"]; + + [mutablePath removeCoordinateAtIndex:index]; + + // update the property + [properties setObject:mutablePath forKey:@"mutablePath"]; + if ([mutablePath count] == 0) { + [properties removeObjectForKey:@"bounds"]; + } else { + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + } + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + +} + + +-(void)setPoints:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSArray *positionList = [command.arguments objectAtIndex:1]; + NSDictionary *latLng; + GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + GMSMutablePath *mutablePath = (GMSMutablePath *)[properties objectForKey:@"mutablePath"]; + [mutablePath removeAllCoordinates]; + for (int i = 0; i < positionList.count; i++) { + latLng = [positionList objectAtIndex:i]; + [mutablePath addCoordinate:CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue])]; + } + + // update the property + [properties setObject:mutablePath forKey:@"mutablePath"]; + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polygon setPath:mutablePath]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + + +-(void)insertPointAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSInteger index = [[command.arguments objectAtIndex:1] integerValue]; + NSDictionary *latLng = [command.arguments objectAtIndex:2]; + GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + GMSMutablePath *mutablePath = (GMSMutablePath *)[properties objectForKey:@"mutablePath"]; + + CLLocationCoordinate2D position = CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue]); + [mutablePath insertCoordinate:position atIndex:index]; + + // update the property + [properties setObject:mutablePath forKey:@"mutablePath"]; + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polygon setPath:mutablePath]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +-(void)setPointAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + NSInteger index = [[command.arguments objectAtIndex:1] integerValue]; + NSDictionary *latLng = [command.arguments objectAtIndex:2]; + GMSPolygon *polygon = (GMSPolygon *)[self.mapCtrl.objects objectForKey:polygonKey]; + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + GMSMutablePath *mutablePath = (GMSMutablePath *)[properties objectForKey:@"mutablePath"]; + + CLLocationCoordinate2D position = CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue]); + [mutablePath replaceCoordinateAtIndex:index withCoordinate:position]; + + // update the property + [properties setObject:mutablePath forKey:@"mutablePath"]; + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polygon setPath:mutablePath]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} +/** + * Set fill color + * @params key + */ +-(void)setFillColor:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + GMSPolygon *polygon = [self.mapCtrl.objects objectForKey:polygonKey]; + + NSArray *rgbColor = [command.arguments objectAtIndex:1]; + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polygon setFillColor:[rgbColor parsePluginColor]]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + + +/** + * Set stroke color + * @params key + */ +-(void)setStrokeColor:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *polygonKey = [command.arguments objectAtIndex:0]; + GMSPolygon *polygon = [self.mapCtrl.objects objectForKey:polygonKey]; + + NSArray *rgbColor = [command.arguments objectAtIndex:1]; + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polygon setStrokeColor:[rgbColor parsePluginColor]]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +/** + * Set stroke width + * @params key + */ +-(void)setStrokeWidth:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *polygonKey = [command.arguments objectAtIndex:0]; + GMSPolygon *polygon = [self.mapCtrl.objects objectForKey:polygonKey]; + float width = [[command.arguments objectAtIndex:1] floatValue]; + + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polygon setStrokeWidth:width]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +/** + * Set z-index + * @params key + */ +-(void)setZIndex:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + GMSPolygon *polygon = [self.mapCtrl.objects objectForKey:polygonKey]; + int zIndex = [[command.arguments objectAtIndex:1] intValue]; + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + // update the property + [properties setObject:[NSNumber numberWithInt:zIndex] forKey:@"zIndex"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polygon setZIndex:(int)zIndex]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +/** + * Set clickable + * @params key + */ +-(void)setClickable:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + //GMSPolygon *polygon = [self.mapCtrl.objects objectForKey:polygonKey]; + Boolean isClickable = [[command.arguments objectAtIndex:1] boolValue]; + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + // update the property + [properties setObject:[NSNumber numberWithBool:isClickable] forKey:@"isClickable"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + +} + +/** + * Set visibility + * @params key + */ +-(void)setVisible:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + GMSPolygon *polygon = [self.mapCtrl.objects objectForKey:polygonKey]; + Boolean isVisible = [[command.arguments objectAtIndex:1] boolValue]; + + // Get properties + NSString *propertyId = [polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + // update the property + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"isVisible"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + if (isVisible) { + polygon.map = self.mapCtrl.map; + } else { + polygon.map = nil; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} +/** + * Set geodesic + * @params key + */ +-(void)setGeodesic:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polygonKey = [command.arguments objectAtIndex:0]; + GMSPolygon *polygon = [self.mapCtrl.objects objectForKey:polygonKey]; + Boolean isGeodisic = [[command.arguments objectAtIndex:1] boolValue]; + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polygon setGeodesic:isGeodisic]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Remove the polygon + * @params key + */ +-(void)remove:(CDVInvokedUrlCommand *)command +{ + + + // Apply to the polygon on UI thread. + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSString *polygonKey = [command.arguments objectAtIndex:0]; + GMSPolygon *polygon = [self.mapCtrl.objects objectForKey:polygonKey]; + [self.mapCtrl.objects removeObjectForKey:[polygonKey stringByReplacingOccurrencesOfString:@"polygon_" withString:@"polygon_property_"]]; + [self.mapCtrl.objects removeObjectForKey:polygonKey]; + polygon.map = nil; + polygon = nil; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolyline.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolyline.h new file mode 100644 index 0000000..461a92d --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolyline.h @@ -0,0 +1,29 @@ +// +// PluginPolyline.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" + +@interface PluginPolyline : CDVPlugin +@property (nonatomic, strong) PluginMapViewController* mapCtrl; +@property (nonatomic) BOOL initialized; + +- (void)create:(CDVInvokedUrlCommand*)command; +- (void)setStrokeColor:(CDVInvokedUrlCommand*)command; +- (void)setStrokeWidth:(CDVInvokedUrlCommand*)command; +- (void)setPoints:(CDVInvokedUrlCommand*)command; +- (void)removePointAt:(CDVInvokedUrlCommand*)command; +- (void)setPointAt:(CDVInvokedUrlCommand*)command; +- (void)insertPointAt:(CDVInvokedUrlCommand*)command; +- (void)setZIndex:(CDVInvokedUrlCommand*)command; +- (void)setVisible:(CDVInvokedUrlCommand*)command; +- (void)setClickable:(CDVInvokedUrlCommand*)command; +- (void)remove:(CDVInvokedUrlCommand*)command; +- (void)setGeodesic:(CDVInvokedUrlCommand*)command; + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolyline.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolyline.m new file mode 100644 index 0000000..3846952 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginPolyline.m @@ -0,0 +1,475 @@ +// +// PluginPolyline.m +// cordova-googlemaps-plugin v2 +// +// Created by masashi. +// +// + +#import "PluginPolyline.h" + +@implementation PluginPolyline + +-(void)setPluginViewController:(PluginViewController *)viewCtrl +{ + self.mapCtrl = (PluginMapViewController *)viewCtrl; +} + +- (void)pluginInitialize +{ + if (self.initialized) { + return; + } + self.initialized = YES; + [super pluginInitialize]; + // Initialize this plugin +} + +- (void)pluginUnload +{ + + // Plugin destroy + NSArray *keys = [self.mapCtrl.objects allKeys]; + NSString *key; + for (int i = 0; i < [keys count]; i++) { + key = [keys objectAtIndex:i]; + if ([key hasPrefix:@"polyline_property"]) { + key = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:key]; + polyline.map = nil; + polyline = nil; + } + [self.mapCtrl.objects removeObjectForKey:key]; + } + + key = nil; + keys = nil; + + NSString *pluginId = [NSString stringWithFormat:@"%@-polyline", self.mapCtrl.overlayId]; + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + [cdvViewController.pluginObjects removeObjectForKey:pluginId]; + [cdvViewController.pluginsMap setValue:nil forKey:pluginId]; + pluginId = nil; +} + +-(void)create:(CDVInvokedUrlCommand *)command +{ + + + NSDictionary *json = [command.arguments objectAtIndex:1]; + NSString *idBase = [command.arguments objectAtIndex:2]; + GMSMutablePath *mutablePath = [GMSMutablePath path]; + + NSArray *points = [json objectForKey:@"points"]; + int i = 0; + NSDictionary *latLng; + for (i = 0; i < points.count; i++) { + latLng = [points objectAtIndex:i]; + [mutablePath + addCoordinate: + CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue])]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + + // Create the Polyline, and assign it to the map. + GMSPolyline *polyline = [GMSPolyline polylineWithPath:mutablePath]; + + + BOOL isVisible = YES; + // Visible property + NSString *visibleValue = [NSString stringWithFormat:@"%@", json[@"visible"]]; + if ([@"0" isEqualToString:visibleValue]) { + // false + isVisible = NO; + polyline.map = nil; + } else { + // true or default + polyline.map = self.mapCtrl.map; + } + BOOL isClickable = NO; + if ([[json valueForKey:@"clickable"] boolValue]) { + isClickable = YES; + } + if ([[json valueForKey:@"geodesic"] boolValue]) { + polyline.geodesic = YES; + } + NSArray *rgbColor = [json valueForKey:@"color"]; + polyline.strokeColor = [rgbColor parsePluginColor]; + + polyline.strokeWidth = [[json valueForKey:@"width"] floatValue]; + polyline.zIndex = [[json valueForKey:@"zIndex"] floatValue]; + + // Since this plugin provide own click detection, + // disable default clickable feature. + polyline.tappable = NO; + + NSString *id = [NSString stringWithFormat:@"polyline_%@", idBase]; + [self.mapCtrl.objects setObject:polyline forKey: id]; + polyline.title = id; + + // Run the below code on background thread. + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + //--------------------------- + // Result for JS + //--------------------------- + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + [result setObject:id forKey:@"__pgmId"]; + + //--------------------------- + // Keep the properties + //--------------------------- + NSString *propertyId = [NSString stringWithFormat:@"polyline_property_%@", idBase]; + + // points + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + [properties setObject:mutablePath forKey:@"mutablePath"]; + // bounds (pre-calculate for click detection) + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + // isVisible + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"isVisible"]; + // isClickable + [properties setObject:[NSNumber numberWithBool:isClickable] forKey:@"isClickable"]; + // geodesic + [properties setObject:[NSNumber numberWithBool:polyline.geodesic] forKey:@"geodesic"]; + // zIndex + [properties setObject:[NSNumber numberWithFloat:polyline.zIndex] forKey:@"zIndex"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }); +} + + + +-(void)removePointAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polylineKey = [command.arguments objectAtIndex:0]; + NSInteger index = [[command.arguments objectAtIndex:1] integerValue]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + + // Get properties + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + GMSMutablePath *mutablePath = (GMSMutablePath *)[properties objectForKey:@"mutablePath"]; + + [mutablePath removeCoordinateAtIndex:index]; + + // update the property + [properties setObject:mutablePath forKey:@"mutablePath"]; + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polyline setPath:mutablePath]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + }]; + +} +-(void)setPoints:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polylineKey = [command.arguments objectAtIndex:0]; + NSArray *positionList = [command.arguments objectAtIndex:1]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + + // Get properties + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + GMSMutablePath *mutablePath = (GMSMutablePath *)[properties objectForKey:@"mutablePath"]; + [mutablePath removeAllCoordinates]; + + CLLocationCoordinate2D position; + NSDictionary *latLng; + for (int i = 0; i < positionList.count; i++) { + latLng = [positionList objectAtIndex:i]; + position = CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue]); + [mutablePath addCoordinate:position]; + } + + // update the property + [properties setObject:mutablePath forKey:@"mutablePath"]; + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polyline setPath:mutablePath]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +-(void)insertPointAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polylineKey = [command.arguments objectAtIndex:0]; + NSInteger index = [[command.arguments objectAtIndex:1] integerValue]; + NSDictionary *latLng = [command.arguments objectAtIndex:2]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + + // Get properties + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + GMSMutablePath *mutablePath = (GMSMutablePath *)[properties objectForKey:@"mutablePath"]; + + CLLocationCoordinate2D position = CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue]); + [mutablePath insertCoordinate:position atIndex:index]; + + // update the property + [properties setObject:mutablePath forKey:@"mutablePath"]; + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polyline setPath:mutablePath]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +-(void)setPointAt:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polylineKey = [command.arguments objectAtIndex:0]; + NSInteger index = [[command.arguments objectAtIndex:1] integerValue]; + NSDictionary *latLng = [command.arguments objectAtIndex:2]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + + // Get properties + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + + GMSMutablePath *mutablePath = (GMSMutablePath *)[properties objectForKey:@"mutablePath"]; + + CLLocationCoordinate2D position = CLLocationCoordinate2DMake([[latLng objectForKey:@"lat"] doubleValue], [[latLng objectForKey:@"lng"] doubleValue]); + [mutablePath replaceCoordinateAtIndex:index withCoordinate:position]; + + // update the property + [properties setObject:mutablePath forKey:@"mutablePath"]; + [properties setObject:[[GMSCoordinateBounds alloc] initWithPath:mutablePath] forKey:@"bounds"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polyline setPath:mutablePath]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +/** + * Set color + * @params key + */ +-(void)setStrokeColor:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *polylineKey = [command.arguments objectAtIndex:0]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + + NSArray *rgbColor = [command.arguments objectAtIndex:1]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polyline setStrokeColor:[rgbColor parsePluginColor]]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +/** + * Set width + * @params key + */ +-(void)setStrokeWidth:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *polylineKey = [command.arguments objectAtIndex:0]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + float width = [[command.arguments objectAtIndex:1] floatValue]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polyline setStrokeWidth:width]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set z-index + * @params key + */ +-(void)setZIndex:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *polylineKey = [command.arguments objectAtIndex:0]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + int zIndex = [[command.arguments objectAtIndex:1] intValue]; + + // Update the property + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithInt:zIndex] forKey:@"zIndex"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + // Run on the UI thread + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polyline setZIndex:(int)zIndex]; + + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Set clickable + * @params key + */ +-(void)setClickable:(CDVInvokedUrlCommand *)command +{ + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + + NSString *polylineKey = [command.arguments objectAtIndex:0]; + //GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + Boolean isClickable = [[command.arguments objectAtIndex:1] boolValue]; + + // Update the property + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:isClickable] forKey:@"isClickable"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} +/** + * Set visibility + * @params key + */ +-(void)setVisible:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *polylineKey = [command.arguments objectAtIndex:0]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + Boolean isVisible = [[command.arguments objectAtIndex:1] boolValue]; + + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:isVisible] forKey:@"isVisible"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + + // Run on the UI thread + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + if (isVisible) { + polyline.map = self.mapCtrl.map; + } else { + polyline.map = nil; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +/** + * Set geodesic + * @params key + */ +-(void)setGeodesic:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *polylineKey = [command.arguments objectAtIndex:0]; + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + Boolean isGeodisic = [[command.arguments objectAtIndex:1] boolValue]; + + // Update the property + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: + [self.mapCtrl.objects objectForKey:propertyId]]; + [properties setObject:[NSNumber numberWithBool:isGeodisic] forKey:@"isGeodisic"]; + [self.mapCtrl.objects setObject:properties forKey:propertyId]; + + + // Run on the UI thread + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [polyline setGeodesic:isGeodisic]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +/** + * Remove the polyline + * @params key + */ +-(void)remove:(CDVInvokedUrlCommand *)command +{ + + [self.mapCtrl.executeQueue addOperationWithBlock:^{ + NSString *polylineKey = [command.arguments objectAtIndex:0]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + GMSPolyline *polyline = (GMSPolyline *)[self.mapCtrl.objects objectForKey:polylineKey]; + [self.mapCtrl.objects removeObjectForKey:polylineKey]; + + NSString *propertyId = [polylineKey stringByReplacingOccurrencesOfString:@"polyline_" withString:@"polyline_property_"]; + [self.mapCtrl.objects removeObjectForKey:propertyId]; + polyline.map = nil; + polyline = nil; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; + +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanorama.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanorama.h new file mode 100644 index 0000000..0b9e9d8 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanorama.h @@ -0,0 +1,30 @@ +// +// PluginStreetViewPanorama.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" +#import "IPluginView.h" +#import "PluginStreetViewPanoramaController.h" + +@interface PluginStreetViewPanorama : CDVPlugin +@property (nonatomic, strong) PluginStreetViewPanoramaController* panoramaCtrl; +@property (nonatomic) BOOL isRemoved; +@property (nonatomic) BOOL initialized; + +- (void)getPanorama:(CDVInvokedUrlCommand*)command; +- (void)attachToWebView:(CDVInvokedUrlCommand*)command; +- (void)detachFromWebView:(CDVInvokedUrlCommand*)command; +- (void)setPov:(CDVInvokedUrlCommand*)command; +- (void)setPosition:(CDVInvokedUrlCommand*)command; +- (void)setPanningGesturesEnabled:(CDVInvokedUrlCommand*)command; +- (void)setZoomGesturesEnabled:(CDVInvokedUrlCommand*)command; +- (void)setNavigationEnabled:(CDVInvokedUrlCommand*)command; +- (void)setStreetNamesEnabled:(CDVInvokedUrlCommand*)command; +- (void)setVisible:(CDVInvokedUrlCommand*)command; +- (void)remove:(CDVInvokedUrlCommand*)command; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanorama.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanorama.m new file mode 100644 index 0000000..fbc07ea --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanorama.m @@ -0,0 +1,245 @@ +// +// PluginStreetViewPanorama.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginStreetViewPanorama.h" + +@implementation PluginStreetViewPanorama + + +- (void)getPanorama:(CDVInvokedUrlCommand *)command { + NSDictionary *meta = [command.arguments objectAtIndex:0]; + self.panoramaCtrl.viewDepth = [[meta objectForKey:@"depth"] integerValue]; + NSDictionary *initOptions = [command.arguments objectAtIndex:1]; + + if ([initOptions objectForKey:@"camera"]) { + [self _setPosition:[initOptions objectForKey:@"camera"]]; + [self _setPov:[initOptions objectForKey:@"camera"]]; + } + if ([initOptions objectForKey:@"gestures"]) { + NSDictionary *gestures = [initOptions objectForKey:@"gestures"]; + if ([gestures objectForKey:@"panning"]) { + self.panoramaCtrl.panoramaView.orientationGestures = [[gestures objectForKey:@"panning"] boolValue]; + } + if ([gestures objectForKey:@"zoom"]) { + self.panoramaCtrl.panoramaView.zoomGestures = [[gestures objectForKey:@"zoom"] boolValue]; + } + } + if ([initOptions objectForKey:@"controls"]) { + NSDictionary *controls = [initOptions objectForKey:@"controls"]; + if ([controls objectForKey:@"navigation"]) { + self.panoramaCtrl.panoramaView.navigationLinksHidden = ![[controls objectForKey:@"navigation"] boolValue]; + } + if ([controls objectForKey:@"streetNames"]) { + self.panoramaCtrl.panoramaView.streetNamesHidden = ![[controls objectForKey:@"streetNames"] boolValue]; + } + } + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + +} + +- (void)setPov:(CDVInvokedUrlCommand *)command { + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSDictionary *cameraOpts = [command.arguments objectAtIndex:0]; + [self _setPov:cameraOpts]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} + +- (void)setPosition:(CDVInvokedUrlCommand *)command { + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + NSDictionary *cameraOpts = [command.arguments objectAtIndex:0]; + [self _setPosition:cameraOpts]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +- (void)_setPosition:(NSDictionary *)cameraOpts { + + if ([cameraOpts valueForKey:@"target"] && [cameraOpts valueForKey:@"target"] != [NSNull null]) { + NSObject *target = [cameraOpts objectForKey:@"target"]; + if ([target isKindOfClass:NSString.class]) { + [self.panoramaCtrl.panoramaView moveToPanoramaID:(NSString *)target]; + } else if ([target isKindOfClass:NSDictionary.class]) { + NSDictionary *latLng = (NSDictionary *)target; + double latitude = [[latLng valueForKey:@"lat"] doubleValue]; + double longitude = [[latLng valueForKey:@"lng"] doubleValue]; + CLLocationCoordinate2D location = CLLocationCoordinate2DMake(latitude, longitude); + + if ([cameraOpts objectForKey:@"source"] && [cameraOpts valueForKey:@"source"] != [NSNull null]) { + GMSPanoramaSource source = [@"OUTDOOR" isEqualToString:[cameraOpts objectForKey:@"source"]] ? + kGMSPanoramaSourceOutside : kGMSPanoramaSourceDefault; + if ([cameraOpts objectForKey:@"radius"]) { + int radius = [[cameraOpts objectForKey:@"radius"] intValue]; + [self.panoramaCtrl.panoramaView moveNearCoordinate:location radius:radius source:source]; + } else { + [self.panoramaCtrl.panoramaView moveNearCoordinate:location source:source]; + } + } else { + if ([cameraOpts objectForKey:@"radius"]) { + int radius = [[cameraOpts objectForKey:@"radius"] intValue]; + [self.panoramaCtrl.panoramaView moveNearCoordinate:location radius:radius]; + } else { + [self.panoramaCtrl.panoramaView moveNearCoordinate:location]; + } + } + } + } + + +} + +- (void)_setPov:(NSDictionary *)cameraOpts { + + + double bearing = self.panoramaCtrl.panoramaView.camera.orientation.heading; + if ([cameraOpts valueForKey:@"bearing"] && [cameraOpts valueForKey:@"bearing"] != [NSNull null]) { + bearing = [[cameraOpts valueForKey:@"bearing"] doubleValue]; + } + + double angle = self.panoramaCtrl.panoramaView.camera.orientation.pitch; + if ([cameraOpts valueForKey:@"tilt"] && [cameraOpts valueForKey:@"tilt"] != [NSNull null]) { + angle = [[cameraOpts valueForKey:@"tilt"] doubleValue]; + } + + float zoom = self.panoramaCtrl.panoramaView.camera.zoom; + if ([cameraOpts valueForKey:@"zoom"] && [cameraOpts valueForKey:@"zoom"] != [NSNull null]) { + zoom = [[cameraOpts valueForKey:@"zoom"] doubleValue]; + } + + self.panoramaCtrl.panoramaView.camera = [GMSPanoramaCamera cameraWithHeading:bearing pitch:angle zoom:zoom FOV:90]; + +} + + +- (void)setPanningGesturesEnabled:(CDVInvokedUrlCommand*)command{ + + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean boolValue = [[command.arguments objectAtIndex:0] boolValue]; + [self.panoramaCtrl.panoramaView setOrientationGestures:boolValue]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +- (void)setZoomGesturesEnabled:(CDVInvokedUrlCommand*)command { + + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean boolValue = [[command.arguments objectAtIndex:0] boolValue]; + [self.panoramaCtrl.panoramaView setZoomGestures:boolValue]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +- (void)setNavigationEnabled:(CDVInvokedUrlCommand*)command { + + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean boolValue = ![[command.arguments objectAtIndex:0] boolValue]; + [self.panoramaCtrl.panoramaView setNavigationLinksHidden:boolValue]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +- (void)setStreetNamesEnabled:(CDVInvokedUrlCommand*)command { + + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean boolValue = ![[command.arguments objectAtIndex:0] boolValue]; + [self.panoramaCtrl.panoramaView setStreetNamesHidden:boolValue]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +- (void)setVisible:(CDVInvokedUrlCommand*)command { + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + Boolean boolValue = ![[command.arguments objectAtIndex:0] boolValue]; + [self.panoramaCtrl.view setHidden:boolValue]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }]; +} +- (void)remove:(CDVInvokedUrlCommand*)command { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + // Load the GoogleMap.m + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + [googlemaps.pluginLayer removePluginOverlay:self.panoramaCtrl]; + self.panoramaCtrl.attached = NO; + self.isRemoved = YES; + self.panoramaCtrl.view = nil; + self.panoramaCtrl = nil; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)pluginUnload { + + // Plugin destroy + self.isRemoved = YES; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + self.panoramaCtrl.view = nil; + self.panoramaCtrl.panoramaView = nil; + self.panoramaCtrl = nil; + }]; +} + +- (void)attachToWebView:(CDVInvokedUrlCommand*)command { + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + + // Load the GoogleMap.m + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + [googlemaps.pluginLayer addPluginOverlay:self.panoramaCtrl]; + self.panoramaCtrl.attached = YES; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)detachFromWebView:(CDVInvokedUrlCommand*)command { + + [self.panoramaCtrl.executeQueue addOperationWithBlock:^{ + + // Load the GoogleMap.m + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + CordovaGoogleMaps *googlemaps = [cdvViewController getCommandInstance:@"CordovaGoogleMaps"]; + [googlemaps.pluginLayer removePluginOverlay:self.panoramaCtrl]; + self.panoramaCtrl.attached = NO; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + +} + + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanoramaController.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanoramaController.h new file mode 100644 index 0000000..6c5f4ea --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanoramaController.h @@ -0,0 +1,58 @@ +// +// PluginStreetViewPanoramaController.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import +#import +#import +#import "PluginUtil.h" +#import "IPluginProtocol.h" +#import "PluginObjects.h" +#import "PluginViewController.h" +#import + +@interface PluginStreetViewPanoramaController : PluginViewController + +@property (nonatomic) GMSPanoramaView* panoramaView; + +//---------------------------------------------------- +// In order to keep the compatibility with Android, +// some delegate methods are commented-out. +//---------------------------------------------------- + +//// Called when starting a move to another panorama. +//- (void)panoramaView:(GMSPanoramaView *)view willMoveToPanoramaID:(NSString *)panoramaID; + +//This is invoked every time the view.panorama property changes. +- (void)panoramaView:(GMSPanoramaView *)view didMoveToPanorama:(nullable GMSPanorama *)panorama; + +// Called when the panorama change was caused by invoking moveToPanoramaNearCoordinate:. +- (void)panoramaView:(GMSPanoramaView *)view didMoveToPanorama:(nonnull GMSPanorama *)panorama nearCoordinate:(CLLocationCoordinate2D)coordinate; + +// Called when moveNearCoordinate: produces an error. +- (void)panoramaView:(GMSPanoramaView *)view error:(nonnull NSError *)error onMoveNearCoordinate:(CLLocationCoordinate2D)coordinate; + +// Called when moveToPanoramaID: produces an error. +- (void)panoramaView:(GMSPanoramaView *)view error:(nonnull NSError *)error onMoveToPanoramaID:(nonnull NSString *)panoramaID; + +// Called repeatedly during changes to the camera on GMSPanoramaView. +- (void)panoramaView:(GMSPanoramaView *)view didMoveCamera:(nonnull GMSPanoramaCamera *)camera; + +// Called when a user has tapped on the GMSPanoramaView, but this tap was not consumed (taps may be consumed by e.g., tapping on a navigation arrow). +- (void)panoramaView:(GMSPanoramaView *)view didTap:(CGPoint)point; + + +//// Called after a marker has been tapped. +//- (void)panoramaView:(GMSPanoramaView *)view didTapMarker:(nonnull GMSMarker *)marker; +// +//// Called when the panorama tiles for the current view have just been requested and are beginning to load. +//- (void)panoramaViewDidStartRendering:(GMSPanoramaView *)panoramaView; +// +//// Called when the panorama tiles have been loaded (or permanently failed to load) and rendered on screen. +//- (void)panoramaViewDidFinishRendering:(GMSPanoramaView *)panoramaView; + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanoramaController.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanoramaController.m new file mode 100644 index 0000000..1ff36e4 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginStreetViewPanoramaController.m @@ -0,0 +1,105 @@ +// +// PluginStreetViewPanoramaController.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginStreetViewPanoramaController.h" + +@implementation PluginStreetViewPanoramaController + +//This is invoked every time the view.panorama property changes. +- (void)panoramaView:(GMSPanoramaView *)view didMoveToPanorama:(nullable GMSPanorama *)panorama { + [self locationChangeEvent:panorama.coordinate]; + +} + +// Called when the panorama change was caused by invoking moveToPanoramaNearCoordinate:. +- (void)panoramaView:(GMSPanoramaView *)view didMoveToPanorama:(nonnull GMSPanorama *)panorama nearCoordinate:(CLLocationCoordinate2D)coordinate { + [self locationChangeEvent:coordinate]; +} + +// Called when moveNearCoordinate: produces an error. +- (void)panoramaView:(GMSPanoramaView *)view error:(nonnull NSError *)error onMoveNearCoordinate:(CLLocationCoordinate2D)coordinate { + [self locationChangeEvent:coordinate]; +} + + +// Called when moveToPanoramaID: produces an error. +- (void)panoramaView:(GMSPanoramaView *)view error:(nonnull NSError *)error onMoveToPanoramaID:(nonnull NSString *)panoramaID { + [self locationChangeEvent:view.panorama.coordinate]; +} + +// Called repeatedly during changes to the camera on GMSPanoramaView. +- (void)panoramaView:(GMSPanoramaView *)view didMoveCamera:(nonnull GMSPanoramaCamera *)camera { + + NSMutableDictionary *cameraInfo = [NSMutableDictionary dictionary]; + [cameraInfo setObject:[NSNumber numberWithDouble:camera.orientation.heading] forKey:@"bearing"]; + [cameraInfo setObject:[NSNumber numberWithDouble:camera.orientation.pitch] forKey:@"tilt"]; + [cameraInfo setObject:[NSNumber numberWithDouble:camera.zoom] forKey:@"zoom"]; + + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:cameraInfo options:0 error:nil]; + NSString* sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onPanoramaCameraChange', args: [%@]});}", + self.overlayId, self.overlayId, @"panorama_camera_change", sourceArrayString]; + [self execJS:jsString]; +} + +// Called when a user has tapped on the GMSPanoramaView, but this tap was not consumed (taps may be consumed by e.g., tapping on a navigation arrow). +- (void)panoramaView:(GMSPanoramaView *)view didTap:(CGPoint)point { + + GMSOrientation svOrientation = [self.panoramaView orientationForPoint:point]; + + NSMutableDictionary *clickInfo = [NSMutableDictionary dictionary]; + NSMutableDictionary *orientaion = [NSMutableDictionary dictionary]; + [orientaion setObject:[NSNumber numberWithDouble:svOrientation.heading] forKey:@"bearing"]; + [orientaion setObject:[NSNumber numberWithDouble:svOrientation.pitch] forKey:@"tilt"]; + [clickInfo setObject:orientaion forKey:@"orientation"]; + + NSMutableArray *pointArray = [NSMutableArray array]; + [pointArray addObject:[NSNumber numberWithInt:point.x]]; + [pointArray addObject:[NSNumber numberWithInt:point.y]]; + [clickInfo setObject:pointArray forKey:@"point"]; + + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:clickInfo options:0 error:nil]; + NSString* sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onPanoramaEvent', args: [%@]});}", + self.overlayId, self.overlayId, @"panorama_click", sourceArrayString]; + [self execJS:jsString]; + +} + +- (void) locationChangeEvent:(CLLocationCoordinate2D)coordinate { + + NSString* sourceArrayString = @"null"; + if (self.panoramaView.panorama) { + NSMutableDictionary *location = [NSMutableDictionary dictionary]; + [location setObject:self.panoramaView.panorama.panoramaID forKey:@"panoId"]; + + NSMutableDictionary *target = [NSMutableDictionary dictionary]; + [target setObject:[NSNumber numberWithDouble:coordinate.latitude] forKey:@"lat"]; + [target setObject:[NSNumber numberWithDouble:coordinate.longitude] forKey:@"lng"]; + [location setObject:target forKey:@"latLng"]; + + NSMutableArray *links = [NSMutableArray array]; + for (GMSPanoramaLink *linkRef in self.panoramaView.panorama.links) { + NSMutableDictionary *link = [NSMutableDictionary dictionary]; + [link setObject:linkRef.panoramaID forKey:@"panoId"]; + [link setObject:[NSNumber numberWithDouble:linkRef.heading] forKey:@"bearing"]; + [links addObject:link]; + } + [location setObject:links forKey:@"links"]; + + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:location options:0 error:nil]; + sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + } + NSString* jsString = [NSString + stringWithFormat:@"javascript:if('%@' in plugin.google.maps){plugin.google.maps['%@']({evtName: '%@', callback: '_onPanoramaLocationChange', args: [%@]});}", + self.overlayId, self.overlayId, @"panorama_location_change", sourceArrayString]; + [self execJS:jsString]; +} +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileOverlay.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileOverlay.h new file mode 100644 index 0000000..c246d49 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileOverlay.h @@ -0,0 +1,27 @@ +// +// PluginTileOverlay.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" +#import "IPluginProtocol.h" +#import "PluginTileProvider.h" + +@interface PluginTileOverlay : CDVPlugin +@property (nonatomic, strong) PluginMapViewController* mapCtrl; +@property (nonatomic) NSOperationQueue *executeQueue; +@property (nonatomic, strong) NSCache* imgCache; +@property (nonatomic) BOOL initialized; + +-(void)create:(CDVInvokedUrlCommand*)command; +-(void)setVisible:(CDVInvokedUrlCommand *)command; +-(void)remove:(CDVInvokedUrlCommand *)command; +-(void)setZIndex:(CDVInvokedUrlCommand *)command; +-(void)setFadeIn:(CDVInvokedUrlCommand *)command; +-(void)setOpacity:(CDVInvokedUrlCommand *)command; +-(void)onGetTileUrlFromJS:(CDVInvokedUrlCommand *)command; + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileOverlay.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileOverlay.m new file mode 100644 index 0000000..7c70b6a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileOverlay.m @@ -0,0 +1,278 @@ +// +// PluginPluginTileOverlay.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginTileOverlay.h" + +@implementation PluginTileOverlay + +-(void)setPluginViewController:(PluginViewController *)viewCtrl +{ + self.mapCtrl = (PluginMapViewController *)viewCtrl; +} +- (void)pluginInitialize +{ + if (self.initialized) { + return; + } + self.initialized = YES; + [super pluginInitialize]; + // Initialize this plugin + self.imgCache = [[NSCache alloc]init]; + self.imgCache.totalCostLimit = 3 * 1024 * 1024 * 1024; // 3MB = Cache for image + self.executeQueue = [NSOperationQueue new]; +} + +- (void)pluginUnload +{ + + if (self.executeQueue != nil){ + self.executeQueue.suspended = YES; + [self.executeQueue cancelAllOperations]; + self.executeQueue.suspended = NO; + self.executeQueue = nil; + } + + + // Plugin destroy + NSArray *keys = [self.mapCtrl.objects allKeys]; + NSString *key; + for (int i = 0; i < [keys count]; i++) { + key = [keys objectAtIndex:i]; + if ([key hasPrefix:@"tileoverlay_property"]) { + key = [key stringByReplacingOccurrencesOfString:@"_property" withString:@""]; + GMSTileLayer *tileoverlay = (GMSTileLayer *)[self.mapCtrl.objects objectForKey:key]; + tileoverlay.map = nil; + tileoverlay = nil; + } + [self.mapCtrl.objects removeObjectForKey:key]; + } + + [self.imgCache removeAllObjects]; + self.imgCache = nil; + + key = nil; + keys = nil; + + NSString *pluginId = [NSString stringWithFormat:@"%@-tileoverlay", self.mapCtrl.overlayId]; + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + [cdvViewController.pluginObjects removeObjectForKey:pluginId]; + [cdvViewController.pluginsMap setValue:nil forKey:pluginId]; + pluginId = nil; +} + +-(void)create:(CDVInvokedUrlCommand *)command +{ + + + dispatch_async(dispatch_get_main_queue(), ^{ + + NSDictionary *json = [command.arguments objectAtIndex:1]; + NSString *idBase = [command.arguments objectAtIndex:2]; + //NSString *tileUrlFormat = [json objectForKey:@"tileUrlFormat"]; + + + GMSTileLayer *layer; + NSString *_id = [NSString stringWithFormat:@"tileoverlay_%@", idBase]; + + //NSRange range = [tileUrlFormat rangeOfString:@"http"]; + //if (range.location != 0) { + NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; + + CDVViewController *cdvViewController = (CDVViewController*)self.viewController; + id webview = cdvViewController.webView; + NSURL *url = [webview URL]; + NSString *webPageUrl = url.absoluteString; + [options setObject:webPageUrl forKey:@"webPageUrl"]; + [options setObject:self.mapCtrl.overlayId forKey:@"mapId"]; + [options setObject:idBase forKey:@"pluginId"]; + + ///[options setObject:tileUrlFormat forKey:@"tileUrlFormat"]; + [options setObject:[json objectForKey:@"tileSize"] forKey:@"tileSize"]; + [options setObject:[json objectForKey:@"debug"] forKey:@"debug"]; + + layer = [[PluginTileProvider alloc] initWithOptions:options webView:webview]; + /* + } else { + GMSTileURLConstructor constructor = ^(NSUInteger x, NSUInteger y, NSUInteger zoom) { + NSString *urlStr = [tileUrlFormat stringByReplacingOccurrencesOfString:@"" withString:[NSString stringWithFormat:@"%lu", (unsigned long)x]]; + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"" withString:[NSString stringWithFormat:@"%lu", (unsigned long)y]]; + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"" withString:[NSString stringWithFormat:@"%lu", (unsigned long)zoom]]; + return [NSURL URLWithString:urlStr]; + }; + + layer = [GMSURLTileLayer tileLayerWithURLConstructor:constructor]; + } + */ + + + + // Visible property + NSString *visibleValue = [NSString stringWithFormat:@"%@", json[@"visible"]]; + if ([@"0" isEqualToString:visibleValue]) { + // false + layer.map = nil; + } else { + // true or default + layer.map = self.mapCtrl.map; + } + if ([json valueForKey:@"zIndex"] && [json valueForKey:@"zIndex"] != [NSNull null]) { + layer.zIndex = [[json valueForKey:@"zIndex"] floatValue]; + } + if ([json valueForKey:@"tileSize"] && [json valueForKey:@"tileSize"] != [NSNull null]) { + layer.tileSize = [[json valueForKey:@"tileSize"] integerValue]; + } + if ([json valueForKey:@"opacity"] && [json valueForKey:@"opacity"] != [NSNull null]) { + layer.opacity = [[json valueForKey:@"opacity"] floatValue]; + } + + + [self.executeQueue addOperationWithBlock:^{ + + [self.mapCtrl.objects setObject:layer forKey:_id]; + + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + [result setObject:_id forKey:@"__pgmId"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + }); +} + + +-(void)onGetTileUrlFromJS:(CDVInvokedUrlCommand *)command +{ + + [self.executeQueue addOperationWithBlock:^{ + NSString *_id = [command.arguments objectAtIndex:0]; + NSString *urlKey = [command.arguments objectAtIndex:1]; + NSString *tileUrl = [command.arguments objectAtIndex:2]; + NSString *pluginId = [NSString stringWithFormat:@"tileoverlay_%@", _id]; + GMSTileLayer *tileLayer = [self.mapCtrl.objects objectForKey:pluginId]; + + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + if (tileLayer) { + PluginTileProvider *localLayer =(PluginTileProvider *)tileLayer; + [localLayer onGetTileUrlFromJS:urlKey tileUrl:tileUrl]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + + }]; +} + +/** + * Set visibility + * @params key + */ +-(void)setVisible:(CDVInvokedUrlCommand *)command +{ + + [self.executeQueue addOperationWithBlock:^{ + NSString *tileLayerKey = [command.arguments objectAtIndex:0]; + GMSTileLayer *layer = (GMSTileLayer *)[self.mapCtrl.objects objectForKey:tileLayerKey]; + Boolean isVisible = [[command.arguments objectAtIndex:1] boolValue]; + dispatch_async(dispatch_get_main_queue(), ^{ + if (isVisible) { + layer.map = self.mapCtrl.map; + } else { + layer.map = nil; + } + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }); + }]; +} + + +/** + * Remove the tile overlay + * @params key + */ +-(void)remove:(CDVInvokedUrlCommand *)command +{ + + [self.executeQueue addOperationWithBlock:^{ + NSString *tileLayerKey = [command.arguments objectAtIndex:0]; + dispatch_async(dispatch_get_main_queue(), ^{ + GMSTileLayer *layer = (GMSTileLayer *)[self.mapCtrl.objects objectForKey:tileLayerKey]; + layer.map = nil; + [layer clearTileCache]; + [self.mapCtrl.objects removeObjectForKey:tileLayerKey]; + layer = nil; + }); + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + +} + + +/** + * Set z-index + * @params key + */ +-(void)setZIndex:(CDVInvokedUrlCommand *)command +{ + [self.executeQueue addOperationWithBlock:^{ + NSString *tileLayerKey = [command.arguments objectAtIndex:0]; + GMSTileLayer *layer = (GMSTileLayer *)[self.mapCtrl.objects objectForKey:tileLayerKey]; + NSInteger zIndex = [[command.arguments objectAtIndex:1] integerValue]; + dispatch_async(dispatch_get_main_queue(), ^{ + [layer setZIndex:(int)zIndex]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }); + }]; +} +/** + * Set fadeIn + * @params key + */ +-(void)setFadeIn:(CDVInvokedUrlCommand *)command +{ + + [self.executeQueue addOperationWithBlock:^{ + NSString *tileLayerKey = [command.arguments objectAtIndex:0]; + GMSTileLayer *layer = (GMSTileLayer *)[self.mapCtrl.objects objectForKey:tileLayerKey]; + Boolean isEnabled = [[command.arguments objectAtIndex:1] boolValue]; + dispatch_async(dispatch_get_main_queue(), ^{ + [layer setFadeIn:isEnabled]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }); + }]; + +} + + +/** + * Set opacity + * @params key + */ +-(void)setOpacity:(CDVInvokedUrlCommand *)command +{ + + [self.executeQueue addOperationWithBlock:^{ + NSString *tileLayerKey = [command.arguments objectAtIndex:0]; + GMSTileLayer *layer = (GMSTileLayer *)[self.mapCtrl.objects objectForKey:tileLayerKey]; + double opacity = [[command.arguments objectAtIndex:1] doubleValue]; + dispatch_async(dispatch_get_main_queue(), ^{ + [layer setOpacity:opacity]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }); + + }]; +} +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileProvider.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileProvider.h new file mode 100644 index 0000000..80c13e2 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileProvider.h @@ -0,0 +1,26 @@ +// +// PluginTileProvider.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "CordovaGoogleMaps.h" + +@interface PluginTileProvider : GMSTileLayer +@property (nonatomic) NSString *tileUrlFormat; +@property (nonatomic) NSString *wwwPath; +@property (nonatomic) UIView *webView; +@property (nonatomic) NSString *mapId; +@property (nonatomic) NSString *pluginId; +@property (nonatomic) NSMutableDictionary *tileUrlMap; +@property (nonatomic) BOOL isDebug; +@property (nonatomic, strong) NSCache* imgCache; +@property (nonatomic) NSOperationQueue *executeQueue; +@property (nonatomic) dispatch_semaphore_t semaphore; +- (id)initWithOptions:(NSDictionary *) options webView:(UIView *)webView; +- (void)requestTileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom receiver:(id)receiver; +- (void)onGetTileUrlFromJS:(NSString *)urlKey tileUrl:(NSString *)tileUrl; +- (void)downloadImageWithX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom url:(NSURL *)url receiver: (id) receiver; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileProvider.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileProvider.m new file mode 100644 index 0000000..f14673e --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginTileProvider.m @@ -0,0 +1,370 @@ +// +// PluginTileProvider.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginTileProvider.h" + +@implementation PluginTileProvider + +CGRect tileRect; +CGRect debugRect; +UIFont *debugFont; +NSDictionary *debugAttributes; + +- (id)initWithOptions:(NSDictionary *) options webView:(UIView *)webView{ + self = [super init]; + self.webView = webView; + //self.tileUrlFormat = [options objectForKey:@"tileUrlFormat"]; + //NSString *urlStr = [options objectForKey:@"wwwPath"]; + + + // Since ionic local server declines HTTP access for some reason, + // replace URL with file path + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *urlStr = [mainBundle pathForResource:@"www/cordova" ofType:@"js"]; + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"/cordova.js" withString:@""]; + + // ionic 4 + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"http://localhost:8080" withString: urlStr]; + + // ionic 5 + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"ionic://localhost" withString: urlStr]; + self.wwwPath = urlStr; + + + if ([options objectForKey:@"tileSize"]) { + self.tileSize = [[options objectForKey:@"tileSize"] floatValue]; + } else { + self.tileSize = 512.0f; + } + self.mapId = [options objectForKey:@"mapId"]; + self.pluginId = [options objectForKey:@"pluginId"]; + self.semaphore = dispatch_semaphore_create(0); + self.tileUrlMap = [NSMutableDictionary dictionary]; + + + self.isDebug = NO; + if ([[options objectForKey:@"debug"] boolValue]) { + self.isDebug = YES; + tileRect = CGRectMake(0, 0 , self.tileSize, self.tileSize); + debugRect = CGRectMake(30, 30 , self.tileSize - 30, self.tileSize - 30); + + debugFont = [UIFont systemFontOfSize:30.0f]; + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + style.lineBreakMode = NSLineBreakByWordWrapping; + + debugAttributes = @{ + NSForegroundColorAttributeName : [UIColor redColor], + NSFontAttributeName : debugFont, + NSParagraphStyleAttributeName : style + }; + } + + self.imgCache = [[NSCache alloc]init]; + self.imgCache.totalCostLimit = 3 * 1024 * 1024 * 1024; // 3MB = Cache for image + self.executeQueue = [NSOperationQueue new]; + + return self; +} + +- (void)execJS: (NSString *)jsString { + if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) { + [self.webView performSelector:@selector(stringByEvaluatingJavaScriptFromString:) withObject:jsString]; + } else if ([self.webView respondsToSelector:@selector(evaluateJavaScript:completionHandler:)]) { + [self.webView performSelector:@selector(evaluateJavaScript:completionHandler:) withObject:jsString withObject:nil]; + } +} + +- (void)onGetTileUrlFromJS:(NSString *)urlKey tileUrl:(NSString *)tileUrl { + @synchronized (self.tileUrlMap) { + [self.tileUrlMap setObject:tileUrl forKey:urlKey]; + dispatch_semaphore_signal(self.semaphore); + } +} + + + +- (void)requestTileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom receiver:(id)receiver { + + + NSString *urlKey = [NSString stringWithFormat:@"%@-%@-%d-%d-%d", + self.mapId, self.pluginId, (int)x, (int)y, (int)zoom]; + + @synchronized (self.semaphore) { + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + // Execute the getTile() callback + NSString* jsString = [NSString + stringWithFormat:@"javascript:cordova.fireDocumentEvent('%@-%@-tileoverlay', {key: \"%@\", x: %d, y: %d, zoom: %d});", + self.mapId, self.pluginId, urlKey, (int)x, (int)y, (int)zoom]; + + [self execJS:jsString]; + + + }]; + dispatch_semaphore_wait(self.semaphore, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(10 * NSEC_PER_SEC))); // Maximum wait 10sec + + } + NSString *urlStr = nil; + @synchronized (self.tileUrlMap) { + urlStr = [self.tileUrlMap objectForKey:urlKey]; + [self.tileUrlMap removeObjectForKey:urlKey]; + } + NSString *originalUrlStr = urlStr; + + if (urlStr == nil || [urlStr containsString:@"(null)"]) { + //------------------------- + // No image tile + //------------------------- + if (self.isDebug) { + UIImage *image = [self drawDebugInfoWithImage:nil + x:x + y:y + zoom:zoom + url: originalUrlStr]; + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + } else { + [receiver receiveTileWithX:x y:y zoom:zoom image:kGMSTileLayerNoTile]; + } + return; + } + + if ([urlStr rangeOfString:@"data:image/"].location != NSNotFound && + [urlStr rangeOfString:@";base64,"].location != NSNotFound) { + + NSArray *tmp = [urlStr componentsSeparatedByString:@","]; + NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:[tmp objectAtIndex:1] options:0]; + + UIImage *image = [[UIImage alloc] initWithData:decodedData]; + if (image.size.width != self.tileSize || image.size.height != self.tileSize) { + image = [image resize:self.tileSize height:self.tileSize]; + } + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + return; + } + + NSRange range = [urlStr rangeOfString:@"http"]; + if (range.location == 0) { + //------------------------- + // http:// or https:// + //------------------------- + [self downloadImageWithX:x y:y zoom:zoom url:[NSURL URLWithString:urlStr] receiver:receiver]; + return; + } + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"file://" withString:@""]; + + // ionic 4 + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"http://localhost:8080" withString: self.wwwPath]; + + // ionic 5 + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"ionic://localhost" withString: self.wwwPath]; + + if ([urlStr hasPrefix:@"http://"] || [urlStr hasPrefix:@"https://"]) { + //------------------------- + // http:// or https:// + //------------------------- + [self downloadImageWithX:x y:y zoom:zoom url:[NSURL URLWithString:urlStr] receiver:receiver]; + return; + } else { + //------------------------- + // Absolute file path + //------------------------- + urlStr = [NSString stringWithFormat:@"file://%@", urlStr]; + + //------------------------- + // file path + //------------------------- + urlStr = [urlStr stringByReplacingOccurrencesOfString:@"file://" withString:@""]; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:urlStr]) { + if (self.isDebug) { + UIImage *image = [self drawDebugInfoWithImage:nil + x:x + y:y + zoom:zoom + url: originalUrlStr]; + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + } else { + [receiver receiveTileWithX:x y:y zoom:zoom image:kGMSTileLayerNoTile]; + } + return; + } + + + NSData *data = [[NSFileManager defaultManager] contentsAtPath:urlStr]; + CGFloat screenScale = [[UIScreen mainScreen] scale]; + UIImage *image = [UIImage imageWithData:data scale:screenScale]; + if (image != nil && + (image.size.width != self.tileSize || image.size.height != self.tileSize)) { + + image = [image resize:self.tileSize height:self.tileSize]; + } + + if (image != nil) { + if (self.isDebug) { + image = [self drawDebugInfoWithImage:image + x:x + y:y + zoom:zoom + url: originalUrlStr]; + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + } else { + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + } + } else { + if (self.isDebug) { + UIImage *image = [self drawDebugInfoWithImage:nil + x:x + y:y + zoom:zoom + url: originalUrlStr]; + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + } else { + [receiver receiveTileWithX:x y:y zoom:zoom image:kGMSTileLayerNoTile]; + } + } + return; + + } + + +} + +- (UIImage*)drawDebugInfoWithImage:(UIImage*)image x:(NSInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom url:(NSString *)url { + + CGSize rectSize = CGSizeMake(self.tileSize, self.tileSize); + UIGraphicsBeginImageContextWithOptions(rectSize, NO, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + if (image != nil) { + [image drawInRect:tileRect]; + //CGContextDrawImage(context, tileRect, image.CGImage); + } + + CGContextSetAllowsAntialiasing(context, true); + CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor); + CGContextSetLineWidth(context, 2); + CGContextMoveToPoint(context, self.tileSize, 0); + CGContextAddLineToPoint(context, 0, 0); + CGContextAddLineToPoint(context, 0, self.tileSize); + CGContextStrokePath(context); + + if (url) { + url = [NSString stringWithFormat:@"\n%@", url]; + } else { + url = @""; + } + + NSString *debugInfo = [NSString stringWithFormat:@"x = %d, y = %d, zoom = %d%@", (int)x, (int)y, (int)zoom, url]; + + [debugInfo drawInRect:debugRect withAttributes:debugAttributes]; + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + image = nil; + return newImage; +} + +- (void)downloadImageWithX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom url:(NSURL *)url receiver: (id) receiver +{ + [self.executeQueue addOperationWithBlock:^{ + + NSURLRequest *req = [NSURLRequest requestWithURL:url + cachePolicy:NSURLRequestReturnCacheDataElseLoad + timeoutInterval:5]; + NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:req]; + if (cachedResponse != nil) { + UIImage *image = [[UIImage alloc] initWithData:cachedResponse.data]; + if (image) { + if (self.isDebug) { + image = [self drawDebugInfoWithImage:image + x:x + y:y + zoom:zoom + url: url.absoluteString]; + } + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + return; + } + } + + NSString *uniqueKey = url.absoluteString; + NSData *cache = [self.imgCache objectForKey:uniqueKey]; + if (cache != nil) { + UIImage *image = [[UIImage alloc] initWithData:cache]; + if (image) { + if (self.isDebug) { + image = [self drawDebugInfoWithImage:image + x:x + y:y + zoom:zoom + url: url.absoluteString]; + } + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + return; + } + } + + //------------------------------------------------------------- + // Use NSURLSessionDataTask instead of [NSURLConnection sendAsynchronousRequest] + // https://stackoverflow.com/a/20871647 + //------------------------------------------------------------- + NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; + NSURLSessionDataTask *getTask = [session dataTaskWithRequest:req + completionHandler:^(NSData *data, NSURLResponse *res, NSError *error) { + [session finishTasksAndInvalidate]; + if ( !error ) { + [self.imgCache setObject:data forKey:uniqueKey cost:data.length]; + UIImage *image = [UIImage imageWithData:data]; + if (self.isDebug) { + image = [self drawDebugInfoWithImage:image + x:x + y:y + zoom:zoom + url: url.absoluteString]; + } + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + } else { + if (self.isDebug) { + UIImage *image = [self drawDebugInfoWithImage:nil + x:x + y:y + zoom:zoom + url: url.absoluteString]; + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + } else { + [receiver receiveTileWithX:x y:y zoom:zoom image:kGMSTileLayerNoTile]; + } + } + + }]; + [getTask resume]; + //------------------------------------------------------------- + // NSURLConnection sendAsynchronousRequest is deprecated. + //------------------------------------------------------------- +/* + [NSURLConnection sendAsynchronousRequest:req + queue:self.executeQueue + completionHandler:^(NSURLResponse *res, NSData *data, NSError *error) { + if ( !error ) { + [self.imgCache setObject:data forKey:uniqueKey cost:data.length]; + UIImage *image = [UIImage imageWithData:data]; + [receiver receiveTileWithX:x y:y zoom:zoom image:image]; + } else { + [receiver receiveTileWithX:x y:y zoom:zoom image:kGMSTileLayerNoTile]; + } + + }]; +*/ + }]; + +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginUtil.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginUtil.h new file mode 100644 index 0000000..d7454ed --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginUtil.h @@ -0,0 +1,90 @@ +// +// PluginUtil.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#ifndef MIN +#import +#endif +#import +#import +#import "MainViewController.h" +#import +#import +//#import "MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.h" +#import "GMSCoordinateBounds+Geometry.h" +#import +#import "IPluginProtocol.h" +#import "PluginViewController.h" +#import +#import + +typedef void (^MYCompletionHandler)(NSError *error); + +// Switch statement in Objective-C +//http://qiita.com/GeneralD/items/5a05f176ac2321e7a51b +#define CASE(str) if ([__s__ isEqualToString:(str)]) +#define SWITCH(s) for (NSString *__s__ = (s); __s__; __s__ = nil) +#define DEFAULT + +@interface UIView (GoogleMapsPlugin) +- (void)setFrameWithDictionary:(NSDictionary *) params; +- (void)setFrameWithInt:(int)left top:(int)top width:(int)width height:(int)height; +@end + +@interface NSArray (GoogleMapsPlugin) +- (UIColor*)parsePluginColor; +@end + +@interface NSString (GoogleMapsPlugin) +- (NSString*)regReplace:(NSString*)pattern replaceTxt:(NSString*)replaceTxt options:(NSRegularExpressionOptions)options; +@end + + +@interface UIImage (GoogleMapsPlugin) +- (UIImage*)imageByApplyingAlpha:(CGFloat) alpha; +- (UIImage *)resize:(CGFloat)width height:(CGFloat)height; +@end + +@interface CDVCommandDelegateImpl (GoogleMapsPlugin) +- (void)hookSendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId; +@end + +// +// animationDidStop for group animation +// http://stackoverflow.com/a/28051909/697856 +// +typedef void (^TIFAnimationGroupCompletionBlock)(); +@interface CAAnimationGroup (Blocks) +- (void)setCompletionBlock:(TIFAnimationGroupCompletionBlock)handler; +@end + + +@interface PluginUtil : NSObject ++ (BOOL)isPolygonContains:(GMSPath *)path coordinate:(CLLocationCoordinate2D)coordinate projection:(GMSProjection *)projection; ++ (CLLocationCoordinate2D)isPointOnTheLine:(GMSPath *)path coordinate:(CLLocationCoordinate2D)coordinate projection:(GMSProjection *)projection; ++ (CLLocationCoordinate2D)isPointOnTheGeodesicLine:(GMSPath *)path coordinate:(CLLocationCoordinate2D)coordinate threshold:(double)threshold projection:(GMSProjection *)projection; ++ (BOOL)isCircleContains:(GMSCircle *)circle coordinate:(CLLocationCoordinate2D)point; ++ (BOOL)isInDebugMode; ++ (GMSMutablePath *)getMutablePathFromCircle:(CLLocationCoordinate2D)center radius:(double)radius; ++ (NSString *)getAbsolutePathFromCDVFilePath:(UIView*)theWebView cdvFilePath:(NSString *)cdvFilePath; ++ (NSString *)PGM_LOCALIZATION:(NSString *)key; +@end + + + +@implementation UIGestureRecognizer (Cancel) +- (void)cancel { + self.enabled = NO; + self.enabled = YES; +} +@end + + + +@interface CDVPlugin (GoogleMapsPlugin) +- (void)setPluginViewController: (PluginViewController*)viewCtrl; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginUtil.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginUtil.m new file mode 100644 index 0000000..b2b283d --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginUtil.m @@ -0,0 +1,469 @@ +// +// PluginUtil.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginUtil.h" +@implementation UIView (GoogleMapsPlugin) +- (void)setFrameWithDictionary:(NSDictionary *)params +{ + float left = [[params objectForKey:@"left"] floatValue]; + float top = [[params objectForKey:@"top"] floatValue]; + float width = [[params objectForKey:@"width"] floatValue]; + float height = [[params objectForKey:@"height"] floatValue]; + + CGRect r = [self frame]; + if (r.origin.x == left && + r.origin.y == top && + r.size.width == width && + r.size.height == height) { + return; + } + r.origin.x = left; + r.origin.y = top; + r.size.width = width; + r.size.height = height; + self.frame = r; +} + + +- (void)setFrameWithInt:(int)left top:(int)top width:(int)width height:(int)height +{ + CGRect r = [self frame]; + if (r.origin.x == left && + r.origin.y == top && + r.size.width == width && + r.size.height == height) { + return; + } + r.origin.x = left; + r.origin.y = top; + r.size.width = width; + r.size.height = height; + self.frame = r; +} + +@end + +@implementation NSArray (GoogleMapsPlugin) +- (UIColor*)parsePluginColor +{ + return [UIColor colorWithRed:[[self objectAtIndex:0] floatValue]/255.0 + green:[[self objectAtIndex:1] floatValue]/255.0 + blue:[[self objectAtIndex:2] floatValue]/255.0 + alpha:[[self objectAtIndex:3] floatValue]/255.0]; + +} +@end + +@implementation NSString (GoogleMapsPlugin) +- (NSString*)regReplace:(NSString*)pattern replaceTxt:(NSString*)replaceTxt options:(NSRegularExpressionOptions)options +{ + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:options error:&error]; + return [regex stringByReplacingMatchesInString:self options:0 range:NSMakeRange(0, [self length]) withTemplate:replaceTxt]; +} +@end + +// Generate a new image with the specified alpha from a uiImage +// http://stackoverflow.com/questions/5084845/how-to-set-the-opacity-alpha-of-a-uiimage#10819117 +@implementation UIImage (GoogleMapsPlugin) +- (UIImage *)imageByApplyingAlpha:(CGFloat) alpha { + UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + CGRect area = CGRectMake(0, 0, self.size.width, self.size.height); + + CGContextScaleCTM(ctx, 1, -1); + CGContextTranslateCTM(ctx, 0, -area.size.height); + + CGContextSetBlendMode(ctx, kCGBlendModeMultiply); + + CGContextSetAlpha(ctx, alpha); + + CGContextDrawImage(ctx, area, self.CGImage); + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + return newImage; +} + +-(UIImage *)resize:(CGFloat)width height:(CGFloat)height { + if (width > 0 && height > 0) { + UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0.0); + [self drawInRect:CGRectMake(0, 0, width, height)]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; + } + return self; +} + + +@end + + +/* +@implementation CDVCommandDelegateImpl (GoogleMapsPlugin) + +- (void)hookSendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId { + + NSRange pos = [callbackId rangeOfString:@"://"]; + if (pos.location == NSNotFound) { + [self sendPluginResult:result callbackId:callbackId]; + } else { + NSArray *tmp = [callbackId componentsSeparatedByString:@"://"]; + NSString *pluginName = [tmp objectAtIndex:0]; + CDVPlugin *plugin = [self getCommandInstance:pluginName]; + [plugin onHookedPluginResult:result callbackId:callbackId]; + } + +} +@end +*/ + +static char CAAnimationGroupBlockKey; +@implementation CAAnimationGroup (Blocks) + +- (void)setCompletionBlock:(TIFAnimationGroupCompletionBlock)handler { + objc_setAssociatedObject(self, &CAAnimationGroupBlockKey, handler, OBJC_ASSOCIATION_COPY_NONATOMIC); + + self.delegate = self; +} + +- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished +{ + if (finished) + { + TIFAnimationGroupCompletionBlock handler = (TIFAnimationGroupCompletionBlock)objc_getAssociatedObject(self, &CAAnimationGroupBlockKey); + if (handler) { + handler(); + } + } +} + +@end + +@implementation PluginUtil + ++ (BOOL)isPolygonContains:(GMSPath *)path coordinate:(CLLocationCoordinate2D)coordinate projection:(GMSProjection *)projection { + //------------------------------------------------------------------- + // Intersects using the Winding Number Algorithm + // http://www.nttpc.co.jp/company/r_and_d/technology/number_algorithm.html + //------------------------------------------------------------------- + int wn = 0; + GMSVisibleRegion visibleRegion = projection.visibleRegion; + GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:visibleRegion]; + CGPoint sw = [projection pointForCoordinate:bounds.southWest]; + + CGPoint touchPoint = [projection pointForCoordinate:coordinate]; + touchPoint.y = sw.y - touchPoint.y; + double vt; + + for (int i = 0; i < [path count] - 1; i++) { + CGPoint a = [projection pointForCoordinate:[path coordinateAtIndex:i]]; + a.y = sw.y - a.y; + CGPoint b = [projection pointForCoordinate:[path coordinateAtIndex:(i + 1)]]; + b.y = sw.y - b.y; + + if ((a.y <= touchPoint.y) && (b.y > touchPoint.y)) { + vt = (touchPoint.y - a.y) / (b.y - a.y); + if (touchPoint.x < (a.x + (vt * (b.x - a.x)))) { + wn++; + } + } else if ((a.y > touchPoint.y) && (b.y <= touchPoint.y)) { + vt = (touchPoint.y - a.y) / (b.y - a.y); + if (touchPoint.x < (a.x + (vt * (b.x - a.x)))) { + wn--; + } + } + } + + return (wn != 0); +} + ++ (CLLocationCoordinate2D)isPointOnTheLine:(GMSPath *)path coordinate:(CLLocationCoordinate2D)coordinate projection:(GMSProjection *)projection { + //------------------------------------------------------------------- + // Intersection for non-geodesic line + // http://movingahead.seesaa.net/article/299962216.html + // http://www.softsurfer.com/Archive/algorithm_0104/algorithm_0104B.htm#Line-Plane + //------------------------------------------------------------------- + double Sx, Sy; + CGPoint touchPoint = [projection pointForCoordinate:coordinate]; + CGPoint p0, p1; + + p0 = [projection pointForCoordinate:[path coordinateAtIndex:0]]; + for (int i = 1; i < [path count]; i++) { + p1 = [projection pointForCoordinate:[path coordinateAtIndex:i]]; + Sx = (touchPoint.x - p0.x) / (p1.x - p0.x); + Sy = (touchPoint.y - p0.y) / (p1.y - p0.y); + if (fabs(Sx - Sy) < 0.05 && Sx < 1 && Sy > 0) { + return [path coordinateAtIndex:i]; + } + } + return kCLLocationCoordinate2DInvalid; +} + ++ (CLLocationCoordinate2D)isPointOnTheGeodesicLine:(GMSPath *)path coordinate:(CLLocationCoordinate2D)point threshold:(double)threshold projection:(GMSProjection *)projection { + + int fingerSize = 40; // assume finger size is 20px + CGPoint touchPoint = [projection pointForCoordinate:CLLocationCoordinate2DMake(point.latitude, point.longitude)]; + GMSCoordinateBounds *possibleBounds = [[GMSCoordinateBounds alloc] init]; + possibleBounds = [possibleBounds includingCoordinate:[projection coordinateForPoint:CGPointMake(touchPoint.x - fingerSize, touchPoint.y - fingerSize)]]; + possibleBounds = [possibleBounds includingCoordinate:[projection coordinateForPoint:CGPointMake(touchPoint.x + fingerSize, touchPoint.y + fingerSize)]]; + + + //------------------------------------------------------------------- + // Intersection for geodesic line + // http://my-clip-devdiary.blogspot.com/2014/01/html5canvas.html + //------------------------------------------------------------------- + double trueDistance, testDistance1, testDistance2; + CGPoint p0, p1; + NSValue *value; + //CGPoint touchPoint = CGPointMake(point.latitude * 100000, point.longitude * 100000); + CLLocationCoordinate2D position1, position2; + CLLocationCoordinate2D start, finish; + NSMutableArray *points = [[NSMutableArray alloc] init]; + BOOL firstTest = NO; + + for (int i = 0; i < [path count]; i++) { + position1 = [path coordinateAtIndex:i]; + p0 = CGPointMake(position1.latitude * 100000, position1.longitude * 100000); + [points addObject:[NSValue valueWithCGPoint:p0]]; + } + for (int i = 0; i < [path count] - 1; i++) { + value = (NSValue *)[points objectAtIndex:i]; + p0 = [value CGPointValue]; + + value = (NSValue *)[points objectAtIndex:(i+1)]; + p1 = [value CGPointValue]; + + position1 = [path coordinateAtIndex:i]; + position2 = [path coordinateAtIndex:(i + 1)]; + + trueDistance = GMSGeometryDistance(position1, position2); + testDistance1 = GMSGeometryDistance(position1, point); + testDistance2 = GMSGeometryDistance(point, position2); + // the distance is exactly same if the point is on the straight line + if (fabs(trueDistance - (testDistance1 + testDistance2)) < threshold) { + start = position1; + finish = position2; + firstTest = YES; + break; + } + } + + if (firstTest == NO) { + return kCLLocationCoordinate2DInvalid; + } + + //---------------------------------------------------------------- + // Calculate waypoints from start to finish on geodesic line + // @ref http://jamesmccaffrey.wordpress.com/2011/04/17/drawing-a-geodesic-line-for-bing-maps-ajax/ + //---------------------------------------------------------------- + + // convert to radians + double lat1 = start.latitude * (M_PI / 180.0); + double lng1 = start.longitude * (M_PI / 180.0); + double lat2 = finish.latitude * (M_PI / 180.0); + double lng2 = finish.longitude * (M_PI / 180.0); + + double d = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + + cos(lat1) * cos(lat2) * pow((sin((lng1 - lng2) / 2)), 2))); + GMSMutablePath *wayPoints = [GMSMutablePath path]; + double f = 0.00000000f; // fraction of the curve + double finc = 0.01000000f; // fraction increment + + while (f <= 1.00000000f) { + double A = sin((1.0 - f) * d) / sin(d); + double B = sin(f * d) / sin(d); + + double x = A * cos(lat1) * cos(lng1) + B * cos(lat2) * cos(lng2); + double y = A * cos(lat1) * sin(lng1) + B * cos(lat2) * sin(lng2); + double z = A * sin(lat1) + B * sin(lat2); + double lat = atan2(z, sqrt((x*x) + (y*y))); + double lng = atan2(y, x); + + CLLocationCoordinate2D wp = CLLocationCoordinate2DMake(lat / (M_PI / 180.0), lng / ( M_PI / 180.0)); + if ([possibleBounds containsCoordinate:wp]) { + [wayPoints addCoordinate:wp]; + } + + f += finc; + } // while + + // break into waypoints with negative longitudes and those with positive longitudes + GMSMutablePath *negLons = [GMSMutablePath path]; // lat-lons where the lon part is negative + GMSMutablePath *posLons = [GMSMutablePath path]; + GMSMutablePath *connect = [GMSMutablePath path]; + + for (int i = 0; i < [wayPoints count]; ++i) { + if ([wayPoints coordinateAtIndex:i].longitude <= 0.0f) + [negLons addCoordinate:[wayPoints coordinateAtIndex:i]]; + else + [posLons addCoordinate:[wayPoints coordinateAtIndex:i]]; + } + + // we may have to connect over 0.0 longitude + for (int i = 0; i < [wayPoints count] - 1; ++i) { + if (([wayPoints coordinateAtIndex:i].longitude <= 0.0f && [wayPoints coordinateAtIndex:(i + 1)].longitude >= 0.0f) || + ([wayPoints coordinateAtIndex:i].longitude >= 0.0f && [wayPoints coordinateAtIndex:(i + 1)].longitude <= 0.0f)) { + if ((fabs([wayPoints coordinateAtIndex:i].longitude) + fabs([wayPoints coordinateAtIndex:(i + 1)].longitude)) < 100.0f) { + [connect addCoordinate:[wayPoints coordinateAtIndex:i]]; + [connect addCoordinate:[wayPoints coordinateAtIndex:(i + 1)]]; + } + } + } + + GMSMutablePath *inspectPoints = [GMSMutablePath path]; + if ([negLons count] > 2) { + for (int i = 0; i < [negLons count]; i++) { + [inspectPoints addCoordinate:[negLons coordinateAtIndex:i]]; + } + } + if ([posLons count] > 2) { + for (int i = 0; i < [posLons count]; i++) { + [inspectPoints addCoordinate:[posLons coordinateAtIndex:i]]; + } + } + if ([connect count] > 2) { + for (int i = 0; i < [connect count]; i++) { + [inspectPoints addCoordinate:[connect coordinateAtIndex:i]]; + } + } + + double minDistance = 999999999; + double distance; + CLLocationCoordinate2D mostClosePoint; + + for (int i = 0; i < [inspectPoints count]; i++) { + distance = GMSGeometryDistance([inspectPoints coordinateAtIndex:i], point); + if (distance < minDistance) { + minDistance = distance; + mostClosePoint = [inspectPoints coordinateAtIndex:i]; + } + } + return mostClosePoint; +} + ++ (BOOL) isInDebugMode +{ + #ifndef __OPTIMIZE__ // Debug Mode + return YES; + #else + return NO; + #endif +} + ++ (BOOL)isCircleContains:(GMSCircle *)circle coordinate:(CLLocationCoordinate2D)point { + CLLocationDistance distance = GMSGeometryDistance(circle.position, point); + return (distance < circle.radius); +} + ++ (GMSMutablePath *)getMutablePathFromCircle:(CLLocationCoordinate2D)center radius:(double)radius { + + double d2r = M_PI / 180; // degrees to radians + double r2d = 180 / M_PI; // radians to degrees + double earthsradius = 3963.189; // 3963 is the radius of the earth in miles + radius = radius * 0.000621371192; // convert to mile + + // find the raidus in lat/lon + double rlat = (radius / earthsradius) * r2d; + double rlng = rlat / cos(center.latitude * d2r); + + GMSMutablePath *mutablePath = [[GMSMutablePath alloc] init]; + double ex, ey; + for (int i = 0; i < 360; i++) { + ey = center.longitude + (rlng * cos(i * d2r)); // center a + radius x * cos(theta) + ex = center.latitude + (rlat * sin(i * d2r)); // center b + radius y * sin(theta) + [mutablePath addLatitude:ex longitude:ey]; + } + return mutablePath; +} + ++ (NSString *)getAbsolutePathFromCDVFilePath:(UIView*)webView cdvFilePath:(NSString *)cdvFilePath { + + NSRange range = [cdvFilePath rangeOfString:@"cdvfile://"]; + if (range.location == NSNotFound) { + return nil; + } + + // Convert cdv:// path to the device real path + // (http://docs.monaca.mobi/3.5/en/reference/phonegap_34/en/file/plugins/) + NSString *filePath = nil; + Class CDVFilesystemURLCls = NSClassFromString(@"CDVFilesystemURL"); + Class CDVFileCls = NSClassFromString(@"CDVFile"); + if (CDVFilesystemURLCls != nil && CDVFileCls != nil) { + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Warc-performSelector-leaks" + + SEL fileSystemURLWithString = NSSelectorFromString(@"fileSystemURLWithString:"); + if ([CDVFilesystemURLCls respondsToSelector:fileSystemURLWithString]) { + id cdvFilesystemURL = [CDVFilesystemURLCls performSelector:fileSystemURLWithString withObject:cdvFilePath]; + if (cdvFilesystemURL != nil) { +#if CORDOVA_VERSION_MIN_REQUIRED >= __CORDOVA_4_0_0 + CDVPlugin *filePlugin = [(CDVPlugin *)[CDVFileCls alloc] init]; +#else + CDVPlugin *filePlugin = (CDVPlugin *)[[CDVFileCls alloc] initWithWebView:webView]; +#endif + [filePlugin pluginInitialize]; + + SEL filesystemPathForURL = NSSelectorFromString(@"filesystemPathForURL:"); + filePath = [filePlugin performSelector: filesystemPathForURL withObject:cdvFilesystemURL]; + } + } + #pragma clang diagnostic pop + } else { + NSLog(@"(debug)File and FileTransfer plugins are required to convert cdvfile:// to localpath."); + } + + return filePath; +} + ++ (NSString *)PGM_LOCALIZATION:(NSString *)key { + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSArray *preferredLanguages = [NSLocale preferredLanguages]; + NSString *localeCode, *languageCode, *path, *filename, *fileContents, *foundFilePath = nil; + NSBundle *mainBundle = [NSBundle mainBundle]; + + for (int i = 0; i < [preferredLanguages count]; i++) { + localeCode = [preferredLanguages objectAtIndex:i]; + + // Find json file for pgm_Localizable_(localeCode).json // pgm_Localizable_en-US.json + filename = [NSString stringWithFormat:@"pgm_Localizable_%@", localeCode]; + path = [mainBundle pathForResource:filename ofType:@"json"]; + if ([fileManager fileExistsAtPath:path]) { + foundFilePath = path; + break; + } + + languageCode = [[localeCode componentsSeparatedByString:@"-"] firstObject]; + // Find json file for pgm_Localizable_(languageCode).json // pgm_Localizable_en.json + filename = [NSString stringWithFormat:@"pgm_Localizable_%@", languageCode]; + path = [mainBundle pathForResource:filename ofType:@"json"]; + if ([fileManager fileExistsAtPath:path]) { + foundFilePath = path; + break; + } + } + + if (!foundFilePath) { + foundFilePath = [mainBundle pathForResource:@"pgm_Localizable_en" ofType:@"json"]; + } + + fileContents = [NSString stringWithContentsOfFile:foundFilePath encoding:NSUTF8StringEncoding error:nil]; + NSData *data = [fileContents dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + NSString *result = [json objectForKey:key]; + if (!result) { + result = key; + } + return result; +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginViewController.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginViewController.h new file mode 100644 index 0000000..80fef8d --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginViewController.h @@ -0,0 +1,37 @@ +// +// PluginViewController.h +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import +#import +#import +#import "PluginUtil.h" +#import "IPluginProtocol.h" +#import "PluginObjects.h" +#import + +@interface PluginViewController : UIViewController + +@property (nonatomic, strong) UIView* webView; +@property (nonatomic) NSMutableDictionary* plugins; +@property (nonatomic) BOOL attached; +@property (nonatomic) BOOL isFullScreen; +@property (nonatomic) BOOL isDragging; +@property (nonatomic) CGRect screenSize; +@property (nonatomic) CGFloat screenScale; +@property (nonatomic) BOOL debuggable; +@property (nonatomic) NSString *overlayId; +@property (nonatomic) BOOL clickable; +@property (nonatomic) BOOL isRenderedAtOnce; +@property (nonatomic, readwrite, strong) NSString *divId; +@property (nonatomic, strong) PluginObjects *objects; +@property (atomic, strong) NSOperationQueue *executeQueue; +@property (nonatomic) NSInteger viewDepth; + +- (id)initWithOptions:(NSDictionary *) options; +- (void)execJS: (NSString *)jsString; +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginViewController.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginViewController.m new file mode 100644 index 0000000..12d9807 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/PluginViewController.m @@ -0,0 +1,62 @@ +// +// PluginStreetViewPanoramaController.m +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// + +#import "PluginStreetViewPanoramaController.h" +#if CORDOVA_VERSION_MIN_REQUIRED < __CORDOVA_4_0_0 +#import +#endif + + +@implementation PluginViewController + +- (id)initWithOptions:(NSDictionary *) options { + self = [super init]; + self.plugins = [NSMutableDictionary dictionary]; + self.isFullScreen = NO; + self.screenSize = [[UIScreen mainScreen] bounds]; + self.screenScale = [[UIScreen mainScreen] scale]; + self.clickable = YES; + self.isRenderedAtOnce = NO; + self.divId = nil; + self.objects = [[PluginObjects alloc] init]; + self.executeQueue = [NSOperationQueue new]; + self.executeQueue.maxConcurrentOperationCount = 10; + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + +} + + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; +} + +- (int)getVideDepth { + return (int)self.viewDepth; +} + +- (void)execJS: (NSString *)jsString { + // Insert setTimeout() in order to prevent the GDC and webView deadlock + // ( you can not click the ok button of Alert() ) + // https://stackoverflow.com/a/23833841/697856 + jsString = [NSString stringWithFormat:@"setTimeout(function(){%@}, 0);", jsString]; + + if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) { + [self.webView performSelector:@selector(stringByEvaluatingJavaScriptFromString:) withObject:jsString]; + } else if ([self.webView respondsToSelector:@selector(evaluateJavaScript:completionHandler:)]) { + [self.webView performSelector:@selector(evaluateJavaScript:completionHandler:) withObject:jsString withObject:nil]; + } +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/TBXML.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/TBXML.h new file mode 100644 index 0000000..36a0f67 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/TBXML.h @@ -0,0 +1,210 @@ +// ================================================================================================ +// TBXML.h +// Fast processing of XML files +// +// ================================================================================================ +// Created by Tom Bradley on 21/10/2009. +// Version 1.5 +// +// Copyright 2012 71Squared All rights reserved.b +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ================================================================================================ + +@class TBXML; + + +// ================================================================================================ +// Error Codes +// ================================================================================================ +enum TBXMLErrorCodes { + D_TBXML_SUCCESS = 0, + + D_TBXML_DATA_NIL, + D_TBXML_DECODE_FAILURE, + D_TBXML_MEMORY_ALLOC_FAILURE, + D_TBXML_FILE_NOT_FOUND_IN_BUNDLE, + + D_TBXML_ELEMENT_IS_NIL, + D_TBXML_ELEMENT_NAME_IS_NIL, + D_TBXML_ELEMENT_NOT_FOUND, + D_TBXML_ELEMENT_TEXT_IS_NIL, + D_TBXML_ATTRIBUTE_IS_NIL, + D_TBXML_ATTRIBUTE_NAME_IS_NIL, + D_TBXML_ATTRIBUTE_NOT_FOUND, + D_TBXML_PARAM_NAME_IS_NIL +}; + + +// ================================================================================================ +// Defines +// ================================================================================================ +#define D_TBXML_DOMAIN @"com.71squared.tbxml" + +#define MAX_ELEMENTS 100 +#define MAX_ATTRIBUTES 100 + +#define TBXML_ATTRIBUTE_NAME_START 0 +#define TBXML_ATTRIBUTE_NAME_END 1 +#define TBXML_ATTRIBUTE_VALUE_START 2 +#define TBXML_ATTRIBUTE_VALUE_END 3 +#define TBXML_ATTRIBUTE_CDATA_END 4 + +// ================================================================================================ +// Structures +// ================================================================================================ + +/** The TBXMLAttribute structure holds information about a single XML attribute. The structure holds the attribute name, value and next sibling attribute. This structure allows us to create a linked list of attributes belonging to a specific element. + */ +typedef struct _TBXMLAttribute { + char * name; + char * value; + struct _TBXMLAttribute * next; +} TBXMLAttribute; + + + +/** The TBXMLElement structure holds information about a single XML element. The structure holds the element name & text along with pointers to the first attribute, parent element, first child element and first sibling element. Using this structure, we can create a linked list of TBXMLElements to map out an entire XML file. + */ +typedef struct _TBXMLElement { + char * name; + char * text; + + TBXMLAttribute * firstAttribute; + + struct _TBXMLElement * parentElement; + + struct _TBXMLElement * firstChild; + struct _TBXMLElement * currentChild; + + struct _TBXMLElement * nextSibling; + struct _TBXMLElement * previousSibling; + +} TBXMLElement; + +/** The TBXMLElementBuffer is a structure that holds a buffer of TBXMLElements. When the buffer of elements is used, an additional buffer is created and linked to the previous one. This allows for efficient memory allocation/deallocation elements. + */ +typedef struct _TBXMLElementBuffer { + TBXMLElement * elements; + struct _TBXMLElementBuffer * next; + struct _TBXMLElementBuffer * previous; +} TBXMLElementBuffer; + + + +/** The TBXMLAttributeBuffer is a structure that holds a buffer of TBXMLAttributes. When the buffer of attributes is used, an additional buffer is created and linked to the previous one. This allows for efficient memeory allocation/deallocation of attributes. + */ +typedef struct _TBXMLAttributeBuffer { + TBXMLAttribute * attributes; + struct _TBXMLAttributeBuffer * next; + struct _TBXMLAttributeBuffer * previous; +} TBXMLAttributeBuffer; + + +// ================================================================================================ +// Block Callbacks +// ================================================================================================ +typedef void (^TBXMLSuccessBlock)(TBXML *tbxml); +typedef void (^TBXMLFailureBlock)(TBXML *tbxml, NSError *error); +typedef void (^TBXMLIterateBlock)(TBXMLElement *element); +typedef void (^TBXMLIterateAttributeBlock)(TBXMLAttribute *attribute, NSString *attributeName, NSString *attributeValue); + + +// ================================================================================================ +// TBXML Public Interface +// ================================================================================================ + +@interface TBXML : NSObject { + +@private + TBXMLElement * rootXMLElement; + + TBXMLElementBuffer * currentElementBuffer; + TBXMLAttributeBuffer * currentAttributeBuffer; + + long currentElement; + long currentAttribute; + + char * bytes; + long bytesLength; +} + + +@property (nonatomic, readonly) TBXMLElement * rootXMLElement; + ++ (id)newTBXMLWithXMLString:(NSString*)aXMLString error:(NSError **)error; ++ (id)newTBXMLWithXMLData:(NSData*)aData error:(NSError **)error; ++ (id)newTBXMLWithXMLFile:(NSString*)aXMLFile error:(NSError **)error; ++ (id)newTBXMLWithXMLFile:(NSString*)aXMLFile fileExtension:(NSString*)aFileExtension error:(NSError **)error; + ++ (id)newTBXMLWithXMLString:(NSString*)aXMLString __attribute__((deprecated)); ++ (id)newTBXMLWithXMLData:(NSData*)aData __attribute__((deprecated)); ++ (id)newTBXMLWithXMLFile:(NSString*)aXMLFile __attribute__((deprecated)); ++ (id)newTBXMLWithXMLFile:(NSString*)aXMLFile fileExtension:(NSString*)aFileExtension __attribute__((deprecated)); + + +- (id)initWithXMLString:(NSString*)aXMLString error:(NSError **)error; +- (id)initWithXMLData:(NSData*)aData error:(NSError **)error; +- (id)initWithXMLFile:(NSString*)aXMLFile error:(NSError **)error; +- (id)initWithXMLFile:(NSString*)aXMLFile fileExtension:(NSString*)aFileExtension error:(NSError **)error; + +- (id)initWithXMLString:(NSString*)aXMLString __attribute__((deprecated)); +- (id)initWithXMLData:(NSData*)aData __attribute__((deprecated)); +- (id)initWithXMLFile:(NSString*)aXMLFile __attribute__((deprecated)); +- (id)initWithXMLFile:(NSString*)aXMLFile fileExtension:(NSString*)aFileExtension __attribute__((deprecated)); + + +- (int) decodeData:(NSData*)data; +- (int) decodeData:(NSData*)data withError:(NSError **)error; + +@end + +// ================================================================================================ +// TBXML Static Functions Interface +// ================================================================================================ + +@interface TBXML (StaticFunctions) + ++ (NSString*) elementName:(TBXMLElement*)aXMLElement; ++ (NSString*) elementName:(TBXMLElement*)aXMLElement error:(NSError **)error; ++ (NSString*) textForElement:(TBXMLElement*)aXMLElement; ++ (NSString*) textForElement:(TBXMLElement*)aXMLElement error:(NSError **)error; ++ (NSString*) valueOfAttributeNamed:(NSString *)aName forElement:(TBXMLElement*)aXMLElement; ++ (NSString*) valueOfAttributeNamed:(NSString *)aName forElement:(TBXMLElement*)aXMLElement error:(NSError **)error; + ++ (NSString*) attributeName:(TBXMLAttribute*)aXMLAttribute; ++ (NSString*) attributeName:(TBXMLAttribute*)aXMLAttribute error:(NSError **)error; ++ (NSString*) attributeValue:(TBXMLAttribute*)aXMLAttribute; ++ (NSString*) attributeValue:(TBXMLAttribute*)aXMLAttribute error:(NSError **)error; + ++ (TBXMLElement*) nextSiblingNamed:(NSString*)aName searchFromElement:(TBXMLElement*)aXMLElement; ++ (TBXMLElement*) childElementNamed:(NSString*)aName parentElement:(TBXMLElement*)aParentXMLElement; + ++ (TBXMLElement*) nextSiblingNamed:(NSString*)aName searchFromElement:(TBXMLElement*)aXMLElement error:(NSError **)error; ++ (TBXMLElement*) childElementNamed:(NSString*)aName parentElement:(TBXMLElement*)aParentXMLElement error:(NSError **)error; + +/** Iterate through all elements found using query. + + Inspiration taken from John Blanco's RaptureXML https://github.com/ZaBlanc/RaptureXML + */ ++ (void)iterateElementsForQuery:(NSString *)query fromElement:(TBXMLElement *)anElement withBlock:(TBXMLIterateBlock)iterateBlock; ++ (void)iterateAttributesOfElement:(TBXMLElement *)anElement withBlock:(TBXMLIterateAttributeBlock)iterateBlock; + + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/TBXML.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/TBXML.m new file mode 100644 index 0000000..7ed54d6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/TBXML.m @@ -0,0 +1,960 @@ +// ================================================================================================ +// TBXML.m +// Fast processing of XML files +// +// ================================================================================================ +// Created by Tom Bradley on 21/10/2009. +// Version 1.5 +// +// Copyright 2012 71Squared All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ================================================================================================ +#import "TBXML.h" + +// ================================================================================================ +// Private methods +// ================================================================================================ +@interface TBXML (Private) ++ (NSString *) errorTextForCode:(int)code; ++ (NSError *) errorWithCode:(int)code; ++ (NSError *) errorWithCode:(int)code userInfo:(NSDictionary *)userInfo; +- (void) decodeBytes; +- (int) allocateBytesOfLength:(long)length error:(NSError **)error; +- (TBXMLElement*) nextAvailableElement; +- (TBXMLAttribute*) nextAvailableAttribute; +@end + +// ================================================================================================ +// Public Implementation +// ================================================================================================ +@implementation TBXML + +@synthesize rootXMLElement; + ++ (id)newTBXMLWithXMLString:(NSString*)aXMLString { + return [[TBXML alloc] initWithXMLString:aXMLString]; +} + ++ (id)newTBXMLWithXMLString:(NSString*)aXMLString error:(NSError *__autoreleasing *)error { + return [[TBXML alloc] initWithXMLString:aXMLString error:error]; +} + ++ (id)newTBXMLWithXMLData:(NSData*)aData { + return [[TBXML alloc] initWithXMLData:aData]; +} + ++ (id)newTBXMLWithXMLData:(NSData*)aData error:(NSError *__autoreleasing *)error { + return [[TBXML alloc] initWithXMLData:aData error:error]; +} + ++ (id)newTBXMLWithXMLFile:(NSString*)aXMLFile { + return [[TBXML alloc] initWithXMLFile:aXMLFile]; +} + ++ (id)newTBXMLWithXMLFile:(NSString*)aXMLFile error:(NSError *__autoreleasing *)error { + return [[TBXML alloc] initWithXMLFile:aXMLFile error:error]; +} + ++ (id)newTBXMLWithXMLFile:(NSString*)aXMLFile fileExtension:(NSString*)aFileExtension { + return [[TBXML alloc] initWithXMLFile:aXMLFile fileExtension:aFileExtension]; +} + ++ (id)newTBXMLWithXMLFile:(NSString*)aXMLFile fileExtension:(NSString*)aFileExtension error:(NSError *__autoreleasing *)error { + return [[TBXML alloc] initWithXMLFile:aXMLFile fileExtension:aFileExtension error:error]; +} + +- (id)init { + self = [super init]; + if (self != nil) { + rootXMLElement = nil; + + currentElementBuffer = 0; + currentAttributeBuffer = 0; + + currentElement = 0; + currentAttribute = 0; + + bytes = 0; + bytesLength = 0; + } + return self; +} +- (id)initWithXMLString:(NSString*)aXMLString { + NSError *error = nil; + return [self initWithXMLString:aXMLString error:&error]; +} + +- (id)initWithXMLString:(NSString*)aXMLString error:(NSError *__autoreleasing *)error { + self = [self init]; + if (self != nil) { + + + // allocate memory for byte array + int result = [self allocateBytesOfLength:[aXMLString lengthOfBytesUsingEncoding:NSUTF8StringEncoding] error:error]; + + // if an error occured, return + if (result != D_TBXML_SUCCESS) + return self; + + // copy string to byte array + [aXMLString getBytes:bytes maxLength:bytesLength usedLength:0 encoding:NSUTF8StringEncoding options:NSStringEncodingConversionAllowLossy range:NSMakeRange(0, bytesLength) remainingRange:nil]; + + // set null terminator at end of byte array + bytes[bytesLength] = 0; + + // decode xml data + [self decodeBytes]; + + // Check for root element + if (error && !*error && !self.rootXMLElement) { + *error = [TBXML errorWithCode:D_TBXML_DECODE_FAILURE]; + } + } + return self; +} + +- (id)initWithXMLData:(NSData*)aData { + NSError *error = nil; + return [self initWithXMLData:aData error:&error]; +} + +- (id)initWithXMLData:(NSData*)aData error:(NSError **)error { + self = [self init]; + if (self != nil) { + // decode aData + [self decodeData:aData withError:error]; + } + + return self; +} + +- (id)initWithXMLFile:(NSString*)aXMLFile { + NSError *error = nil; + return [self initWithXMLFile:aXMLFile error:&error]; +} + +- (id)initWithXMLFile:(NSString*)aXMLFile error:(NSError **)error { + NSString * filename = [aXMLFile stringByDeletingPathExtension]; + NSString * extension = [aXMLFile pathExtension]; + + self = [self initWithXMLFile:filename fileExtension:extension error:error]; + + return self; +} + +- (id)initWithXMLFile:(NSString*)aXMLFile fileExtension:(NSString*)aFileExtension { + NSError *error = nil; + return [self initWithXMLFile:aXMLFile fileExtension:aFileExtension error:&error]; +} + +- (id)initWithXMLFile:(NSString*)aXMLFile fileExtension:(NSString*)aFileExtension error:(NSError **)error { + self = [self init]; + if (self != nil) { + + NSData * data; + + // Get the bundle that this class resides in. This allows to load resources from the app bundle when running unit tests. + NSString * bundlePath = nil; + NSRange range = [aXMLFile rangeOfString:@"/"]; + if (range.location == 0) { + bundlePath = [NSString stringWithFormat:@"%@.%@", aXMLFile, aFileExtension]; + } else { + bundlePath =[[NSBundle bundleForClass:[self class]] pathForResource:aXMLFile ofType:aFileExtension]; + } + + if (!bundlePath) { + if (error) { + NSDictionary * userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[aXMLFile stringByAppendingPathExtension:aFileExtension], NSFilePathErrorKey, nil]; + *error = [TBXML errorWithCode:D_TBXML_FILE_NOT_FOUND_IN_BUNDLE userInfo:userInfo]; + } + } else { + SEL dataWithUncompressedContentsOfFile = NSSelectorFromString(@"dataWithUncompressedContentsOfFile:"); + + // Get uncompressed file contents if TBXML+Compression has been included + if ([[NSData class] respondsToSelector:dataWithUncompressedContentsOfFile]) { + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Warc-performSelector-leaks" + data = [[NSData class] performSelector:dataWithUncompressedContentsOfFile withObject:bundlePath]; + #pragma clang diagnostic pop + + } else { + data = [NSData dataWithContentsOfFile:bundlePath]; + } + + // decode data + [self decodeData:data withError:error]; + + // Check for root element + if (error && !*error && !self.rootXMLElement) { + *error = [TBXML errorWithCode:D_TBXML_DECODE_FAILURE]; + } + } + } + return self; +} + +- (int) decodeData:(NSData*)data { + NSError *error = nil; + return [self decodeData:data withError:&error]; +} + +- (int) decodeData:(NSData*)data withError:(NSError **)error { + + NSError *localError = nil; + + // allocate memory for byte array + int result = [self allocateBytesOfLength:[data length] error:&localError]; + + // ensure no errors during allocation + if (result == D_TBXML_SUCCESS) { + + // copy data to byte array + [data getBytes:bytes length:bytesLength]; + + // set null terminator at end of byte array + bytes[bytesLength] = 0; + + // decode xml data + [self decodeBytes]; + + if (!self.rootXMLElement) { + localError = [TBXML errorWithCode:D_TBXML_DECODE_FAILURE]; + } + } + + // assign local error to pointer + if (error) *error = localError; + + // return success or error code + return localError == nil ? D_TBXML_SUCCESS : (int)[localError code]; +} + +@end + + +// ================================================================================================ +// Static Functions Implementation +// ================================================================================================ + +#pragma mark - +#pragma mark Static Functions implementation + +@implementation TBXML (StaticFunctions) + ++ (NSString*) elementName:(TBXMLElement*)aXMLElement { + if (nil == aXMLElement->name) return @""; + return [NSString stringWithCString:&aXMLElement->name[0] encoding:NSUTF8StringEncoding]; +} + ++ (NSString*) elementName:(TBXMLElement*)aXMLElement error:(NSError **)error { + // check for nil element + if (nil == aXMLElement) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_IS_NIL]; + return @""; + } + + // check for nil element name + if (nil == aXMLElement->name || strlen(aXMLElement->name) == 0) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_NAME_IS_NIL]; + return @""; + } + + return [NSString stringWithCString:&aXMLElement->name[0] encoding:NSUTF8StringEncoding]; +} + ++ (NSString*) attributeName:(TBXMLAttribute*)aXMLAttribute { + if (nil == aXMLAttribute->name) return @""; + return [NSString stringWithCString:&aXMLAttribute->name[0] encoding:NSUTF8StringEncoding]; +} + ++ (NSString*) attributeName:(TBXMLAttribute*)aXMLAttribute error:(NSError **)error { + // check for nil attribute + if (nil == aXMLAttribute) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ATTRIBUTE_IS_NIL]; + return @""; + } + + // check for nil attribute name + if (nil == aXMLAttribute->name) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ATTRIBUTE_NAME_IS_NIL]; + return @""; + } + + return [NSString stringWithCString:&aXMLAttribute->name[0] encoding:NSUTF8StringEncoding]; +} + + ++ (NSString*) attributeValue:(TBXMLAttribute*)aXMLAttribute { + if (nil == aXMLAttribute->value) return @""; + return [NSString stringWithCString:&aXMLAttribute->value[0] encoding:NSUTF8StringEncoding]; +} + ++ (NSString*) attributeValue:(TBXMLAttribute*)aXMLAttribute error:(NSError **)error { + // check for nil attribute + if (nil == aXMLAttribute) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ATTRIBUTE_IS_NIL]; + return @""; + } + + return [NSString stringWithCString:&aXMLAttribute->value[0] encoding:NSUTF8StringEncoding]; +} + ++ (NSString*) textForElement:(TBXMLElement*)aXMLElement { + if (nil == aXMLElement->text) return @""; + return [NSString stringWithCString:&aXMLElement->text[0] encoding:NSUTF8StringEncoding]; +} + ++ (NSString*) textForElement:(TBXMLElement*)aXMLElement error:(NSError **)error { + // check for nil element + if (nil == aXMLElement) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_IS_NIL]; + return @""; + } + + // check for nil text value + if (nil == aXMLElement->text || strlen(aXMLElement->text) == 0) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_TEXT_IS_NIL]; + return @""; + } + + return [NSString stringWithCString:&aXMLElement->text[0] encoding:NSUTF8StringEncoding]; +} + ++ (NSString*) valueOfAttributeNamed:(NSString *)aName forElement:(TBXMLElement*)aXMLElement { + const char * name = [aName cStringUsingEncoding:NSUTF8StringEncoding]; + NSString * value = nil; + TBXMLAttribute * attribute = aXMLElement->firstAttribute; + while (attribute) { + if (strlen(attribute->name) == strlen(name) && memcmp(attribute->name,name,strlen(name)) == 0) { + value = [NSString stringWithCString:&attribute->value[0] encoding:NSUTF8StringEncoding]; + break; + } + attribute = attribute->next; + } + return value; +} + ++ (NSString*) valueOfAttributeNamed:(NSString *)aName forElement:(TBXMLElement*)aXMLElement error:(NSError **)error { + // check for nil element + if (nil == aXMLElement) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_IS_NIL]; + return @""; + } + + // check for nil name parameter + if (nil == aName) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ATTRIBUTE_NAME_IS_NIL]; + return @""; + } + + const char * name = [aName cStringUsingEncoding:NSUTF8StringEncoding]; + NSString * value = nil; + + TBXMLAttribute * attribute = aXMLElement->firstAttribute; + while (attribute) { + if (strlen(attribute->name) == strlen(name) && memcmp(attribute->name,name,strlen(name)) == 0) { + if (attribute->value[0]) + value = [NSString stringWithCString:&attribute->value[0] encoding:NSUTF8StringEncoding]; + else + value = @""; + + break; + } + attribute = attribute->next; + } + + // check for attribute not found + if (!value) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ATTRIBUTE_NOT_FOUND]; + return @""; + } + + return value; +} + ++ (TBXMLElement*) childElementNamed:(NSString*)aName parentElement:(TBXMLElement*)aParentXMLElement{ + + TBXMLElement * xmlElement = aParentXMLElement->firstChild; + const char * name = [aName cStringUsingEncoding:NSUTF8StringEncoding]; + while (xmlElement) { + if (strlen(xmlElement->name) == strlen(name) && memcmp(xmlElement->name,name,strlen(name)) == 0) { + return xmlElement; + } + xmlElement = xmlElement->nextSibling; + } + return nil; +} + ++ (TBXMLElement*) childElementNamed:(NSString*)aName parentElement:(TBXMLElement*)aParentXMLElement error:(NSError **)error { + // check for nil element + if (nil == aParentXMLElement) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_IS_NIL]; + return nil; + } + + // check for nil name parameter + if (nil == aName) { + if (error) *error = [TBXML errorWithCode:D_TBXML_PARAM_NAME_IS_NIL]; + return nil; + } + + TBXMLElement * xmlElement = aParentXMLElement->firstChild; + const char * name = [aName cStringUsingEncoding:NSUTF8StringEncoding]; + while (xmlElement) { + if (strlen(xmlElement->name) == strlen(name) && memcmp(xmlElement->name,name,strlen(name)) == 0) { + return xmlElement; + } + xmlElement = xmlElement->nextSibling; + } + + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_NOT_FOUND]; + + return nil; +} + ++ (TBXMLElement*) nextSiblingNamed:(NSString*)aName searchFromElement:(TBXMLElement*)aXMLElement{ + TBXMLElement * xmlElement = aXMLElement->nextSibling; + const char * name = [aName cStringUsingEncoding:NSUTF8StringEncoding]; + while (xmlElement) { + if (strlen(xmlElement->name) == strlen(name) && memcmp(xmlElement->name,name,strlen(name)) == 0) { + return xmlElement; + } + xmlElement = xmlElement->nextSibling; + } + return nil; +} + ++ (TBXMLElement*) nextSiblingNamed:(NSString*)aName searchFromElement:(TBXMLElement*)aXMLElement error:(NSError **)error { + // check for nil element + if (nil == aXMLElement) { + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_IS_NIL]; + return nil; + } + + // check for nil name parameter + if (nil == aName) { + if (error) *error = [TBXML errorWithCode:D_TBXML_PARAM_NAME_IS_NIL]; + return nil; + } + + TBXMLElement * xmlElement = aXMLElement->nextSibling; + const char * name = [aName cStringUsingEncoding:NSUTF8StringEncoding]; + while (xmlElement) { + if (strlen(xmlElement->name) == strlen(name) && memcmp(xmlElement->name,name,strlen(name)) == 0) { + return xmlElement; + } + xmlElement = xmlElement->nextSibling; + } + + if (error) *error = [TBXML errorWithCode:D_TBXML_ELEMENT_NOT_FOUND]; + + return nil; +} + ++ (void)iterateElementsForQuery:(NSString *)query fromElement:(TBXMLElement *)anElement withBlock:(TBXMLIterateBlock)iterateBlock { + + NSArray *components = [query componentsSeparatedByString:@"."]; + TBXMLElement *currTBXMLElement = anElement; + + // navigate down + for (NSInteger i=0; i < components.count; ++i) { + NSString *iTagName = [components objectAtIndex:i]; + + if ([iTagName isEqualToString:@"*"]) { + currTBXMLElement = currTBXMLElement->firstChild; + + // different behavior depending on if this is the end of the query or midstream + if (i < (components.count - 1)) { + // midstream + do { + NSString *restOfQuery = [[components subarrayWithRange:NSMakeRange(i + 1, components.count - i - 1)] componentsJoinedByString:@"."]; + [TBXML iterateElementsForQuery:restOfQuery fromElement:currTBXMLElement withBlock:iterateBlock]; + } while ((currTBXMLElement = currTBXMLElement->nextSibling)); + + } + } else { + currTBXMLElement = [TBXML childElementNamed:iTagName parentElement:currTBXMLElement]; + } + + if (!currTBXMLElement) { + break; + } + } + + if (currTBXMLElement) { + // enumerate + NSString *childTagName = [components lastObject]; + + if ([childTagName isEqualToString:@"*"]) { + childTagName = nil; + } + + do { + iterateBlock(currTBXMLElement); + } while (childTagName ? (currTBXMLElement = [TBXML nextSiblingNamed:childTagName searchFromElement:currTBXMLElement]) : (currTBXMLElement = currTBXMLElement->nextSibling)); + } +} + ++ (void)iterateAttributesOfElement:(TBXMLElement *)anElement withBlock:(TBXMLIterateAttributeBlock)iterateAttributeBlock { + + // Obtain first attribute from element + TBXMLAttribute * attribute = anElement->firstAttribute; + + // if attribute is valid + + while (attribute) { + // Call the iterateAttributeBlock with the attribute, it's name and value + iterateAttributeBlock(attribute, [TBXML attributeName:attribute], [TBXML attributeValue:attribute]); + + // Obtain the next attribute + attribute = attribute->next; + } +} + +@end + + +// ================================================================================================ +// Private Implementation +// ================================================================================================ + +#pragma mark - +#pragma mark Private implementation + +@implementation TBXML (Private) + ++ (NSString *) errorTextForCode:(int)code { + NSString * codeText = @""; + + switch (code) { + case D_TBXML_DATA_NIL: codeText = @"Data is nil"; break; + case D_TBXML_DECODE_FAILURE: codeText = @"Decode failure"; break; + case D_TBXML_MEMORY_ALLOC_FAILURE: codeText = @"Unable to allocate memory"; break; + case D_TBXML_FILE_NOT_FOUND_IN_BUNDLE: codeText = @"File not found in bundle"; break; + + case D_TBXML_ELEMENT_IS_NIL: codeText = @"Element is nil"; break; + case D_TBXML_ELEMENT_NAME_IS_NIL: codeText = @"Element name is nil"; break; + case D_TBXML_ATTRIBUTE_IS_NIL: codeText = @"Attribute is nil"; break; + case D_TBXML_ATTRIBUTE_NAME_IS_NIL: codeText = @"Attribute name is nil"; break; + case D_TBXML_ELEMENT_TEXT_IS_NIL: codeText = @"Element text is nil"; break; + case D_TBXML_PARAM_NAME_IS_NIL: codeText = @"Parameter name is nil"; break; + case D_TBXML_ATTRIBUTE_NOT_FOUND: codeText = @"Attribute not found"; break; + case D_TBXML_ELEMENT_NOT_FOUND: codeText = @"Element not found"; break; + + default: codeText = @"No Error Description!"; break; + } + + return codeText; +} + ++ (NSError *) errorWithCode:(int)code { + + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[TBXML errorTextForCode:code], NSLocalizedDescriptionKey, nil]; + + return [NSError errorWithDomain:D_TBXML_DOMAIN + code:code + userInfo:userInfo]; +} + ++ (NSError *) errorWithCode:(int)code userInfo:(NSMutableDictionary *)someUserInfo { + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:someUserInfo]; + [userInfo setValue:[TBXML errorTextForCode:code] forKey:NSLocalizedDescriptionKey]; + + return [NSError errorWithDomain:D_TBXML_DOMAIN + code:code + userInfo:userInfo]; +} + +- (int) allocateBytesOfLength:(long)length error:(NSError **)error { + bytesLength = length; + + NSError *localError = nil; + + if(!length) { + localError = [TBXML errorWithCode:D_TBXML_DATA_NIL]; + } + + bytes = malloc(bytesLength+1); + + if(!bytes) { + localError = [TBXML errorWithCode:D_TBXML_MEMORY_ALLOC_FAILURE]; + } + + if (error) *error = localError; + + return localError == nil ? D_TBXML_SUCCESS : (int)[localError code]; +} + +- (void) decodeBytes { + + // ----------------------------------------------------------------------------- + // Process xml + // ----------------------------------------------------------------------------- + + // set elementStart pointer to the start of our xml + char * elementStart=bytes; + + // set parent element to nil + TBXMLElement * parentXMLElement = nil; + + // find next element start + while ((elementStart = strstr(elementStart,"<"))) { + + // detect comment section + if (strncmp(elementStart,"") + 3; + continue; + } + + // detect cdata section within element text + int isCDATA = strncmp(elementStart,""); + + // find start of next element skipping any cdata sections within text + char * elementEnd = CDATAEnd; + + // find next open tag + elementEnd = strstr(elementEnd,"<"); + // if open tag is a cdata section + while (strncmp(elementEnd,""); + // find next open tag + elementEnd = strstr(elementEnd,"<"); + } + + // calculate length of cdata content + long CDATALength = CDATAEnd-elementStart; + + // calculate total length of text + long textLength = elementEnd-elementStart; + + // remove begining cdata section tag + memcpy(elementStart, elementStart+9, CDATAEnd-elementStart-9); + + // remove ending cdata section tag + memcpy(CDATAEnd-9, CDATAEnd+3, textLength-CDATALength-3); + + // blank out end of text + memset(elementStart+textLength-12,' ',12); + + // set new search start position + elementStart = CDATAEnd-9; + continue; + } + + + // find element end, skipping any cdata sections within attributes + char * elementEnd = elementStart+1; + while ((elementEnd = strpbrk(elementEnd, "<>"))) { + if (strncmp(elementEnd,"")+3; + } else { + break; + } + } + + if (!elementEnd) break; + + // null terminate element end + if (elementEnd) *elementEnd = 0; + + // null terminate element start so previous element text doesnt overrun + *elementStart = 0; + + // get element name start + char * elementNameStart = elementStart+1; + + // ignore tags that start with ? or ! unless cdata "text) { + // trim whitespace from start of text + while (isspace(*parentXMLElement->text)) + parentXMLElement->text++; + + // trim whitespace from end of text + char * end = parentXMLElement->text + strlen(parentXMLElement->text)-1; + while (end > parentXMLElement->text && isspace(*end)) + *end--=0; + } + + parentXMLElement = parentXMLElement->parentElement; + + // if parent element has children clear text + if (parentXMLElement && parentXMLElement->firstChild) + parentXMLElement->text = 0; + + } + continue; + } + + + // is this element opening and closing + BOOL selfClosingElement = NO; + if (*(elementEnd-1) == '/') { + selfClosingElement = YES; + } + + + // create new xmlElement struct + TBXMLElement * xmlElement = [self nextAvailableElement]; + + // set element name + xmlElement->name = elementNameStart; + + // if there is a parent element + if (parentXMLElement) { + + // if this is first child of parent element + if (parentXMLElement->currentChild) { + // set next child element in list + parentXMLElement->currentChild->nextSibling = xmlElement; + xmlElement->previousSibling = parentXMLElement->currentChild; + + parentXMLElement->currentChild = xmlElement; + + + } else { + // set first child element + parentXMLElement->currentChild = xmlElement; + parentXMLElement->firstChild = xmlElement; + } + + xmlElement->parentElement = parentXMLElement; + } + + + // in the following xml the ">" is replaced with \0 by elementEnd. + // element may contain no atributes and would return nil while looking for element name end + // + // find end of element name + char * elementNameEnd = strpbrk(xmlElement->name," /\n"); + + + // if end was found check for attributes + if (elementNameEnd) { + + // null terminate end of elemenet name + *elementNameEnd = 0; + + char * chr = elementNameEnd; + char * name = nil; + char * value = nil; + char * CDATAStart = nil; + char * CDATAEnd = nil; + TBXMLAttribute * lastXMLAttribute = nil; + TBXMLAttribute * xmlAttribute = nil; + BOOL singleQuote = NO; + + int mode = TBXML_ATTRIBUTE_NAME_START; + + // loop through all characters within element + while (chr++ < elementEnd) { + + switch (mode) { + // look for start of attribute name + case TBXML_ATTRIBUTE_NAME_START: + if (isspace(*chr)) continue; + name = chr; + mode = TBXML_ATTRIBUTE_NAME_END; + break; + // look for end of attribute name + case TBXML_ATTRIBUTE_NAME_END: + if (isspace(*chr) || *chr == '=') { + *chr = 0; + mode = TBXML_ATTRIBUTE_VALUE_START; + } + break; + // look for start of attribute value + case TBXML_ATTRIBUTE_VALUE_START: + if (isspace(*chr)) continue; + if (*chr == '"' || *chr == '\'') { + value = chr+1; + mode = TBXML_ATTRIBUTE_VALUE_END; + if (*chr == '\'') + singleQuote = YES; + else + singleQuote = NO; + } + break; + // look for end of attribute value + case TBXML_ATTRIBUTE_VALUE_END: + if (*chr == '<' && strncmp(chr, ""); + + // remove end cdata tag + memcpy(CDATAEnd, CDATAEnd+3, strlen(CDATAEnd)-2); + } + + + // create new attribute + xmlAttribute = [self nextAvailableAttribute]; + + // if this is the first attribute found, set pointer to this attribute on element + if (!xmlElement->firstAttribute) xmlElement->firstAttribute = xmlAttribute; + // if previous attribute found, link this attribute to previous one + if (lastXMLAttribute) lastXMLAttribute->next = xmlAttribute; + // set last attribute to this attribute + lastXMLAttribute = xmlAttribute; + + // set attribute name & value + xmlAttribute->name = name; + xmlAttribute->value = value; + + // clear name and value pointers + name = nil; + value = nil; + + // start looking for next attribute + mode = TBXML_ATTRIBUTE_NAME_START; + } + break; + // look for end of cdata + case TBXML_ATTRIBUTE_CDATA_END: + if (*chr == ']') { + if (strncmp(chr, "]]>", 3) == 0) { + mode = TBXML_ATTRIBUTE_VALUE_END; + } + } + break; + default: + break; + } + } + } + + // if tag is not self closing, set parent to current element + if (!selfClosingElement) { + // set text on element to element end+1 + if (*(elementEnd+1) != '>') + xmlElement->text = elementEnd+1; + + parentXMLElement = xmlElement; + } + + // start looking for next element after end of current element + elementStart = elementEnd+1; + } +} + +// Deallocate used memory +- (void) dealloc { + + if (bytes) { + free(bytes); + bytes = nil; + } + + while (currentElementBuffer) { + if (currentElementBuffer->elements) + free(currentElementBuffer->elements); + + if (currentElementBuffer->previous) { + currentElementBuffer = currentElementBuffer->previous; + free(currentElementBuffer->next); + } else { + free(currentElementBuffer); + currentElementBuffer = 0; + } + } + + while (currentAttributeBuffer) { + if (currentAttributeBuffer->attributes) + free(currentAttributeBuffer->attributes); + + if (currentAttributeBuffer->previous) { + currentAttributeBuffer = currentAttributeBuffer->previous; + free(currentAttributeBuffer->next); + } else { + free(currentAttributeBuffer); + currentAttributeBuffer = 0; + } + } + +//#ifndef ARC_ENABLED +// [super dealloc]; +//#endif +} + +- (TBXMLElement*) nextAvailableElement { + currentElement++; + + if (!currentElementBuffer) { + currentElementBuffer = calloc(1, sizeof(TBXMLElementBuffer)); + currentElementBuffer->elements = (TBXMLElement*)calloc(1,sizeof(TBXMLElement)*MAX_ELEMENTS); + currentElement = 0; + rootXMLElement = ¤tElementBuffer->elements[currentElement]; + } else if (currentElement >= MAX_ELEMENTS) { + currentElementBuffer->next = calloc(1, sizeof(TBXMLElementBuffer)); + currentElementBuffer->next->previous = currentElementBuffer; + currentElementBuffer = currentElementBuffer->next; + currentElementBuffer->elements = (TBXMLElement*)calloc(1,sizeof(TBXMLElement)*MAX_ELEMENTS); + currentElement = 0; + } + + return ¤tElementBuffer->elements[currentElement]; +} + +- (TBXMLAttribute*) nextAvailableAttribute { + currentAttribute++; + + if (!currentAttributeBuffer) { + currentAttributeBuffer = calloc(1, sizeof(TBXMLAttributeBuffer)); + currentAttributeBuffer->attributes = (TBXMLAttribute*)calloc(MAX_ATTRIBUTES,sizeof(TBXMLAttribute)); + currentAttribute = 0; + } else if (currentAttribute >= MAX_ATTRIBUTES) { + currentAttributeBuffer->next = calloc(1, sizeof(TBXMLAttributeBuffer)); + currentAttributeBuffer->next->previous = currentAttributeBuffer; + currentAttributeBuffer = currentAttributeBuffer->next; + currentAttributeBuffer->attributes = (TBXMLAttribute*)calloc(MAX_ATTRIBUTES,sizeof(TBXMLAttribute)); + currentAttribute = 0; + } + + return ¤tAttributeBuffer->attributes[currentAttribute]; +} + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/UIImageCache.h b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/UIImageCache.h new file mode 100644 index 0000000..919b96d --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/UIImageCache.h @@ -0,0 +1,29 @@ +// +// UIImageCache +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// +#ifndef UIImageCache_h +#define UIImageCache_h + +#import + +@interface UIImageCache : NSObject + ++ (UIImageCache*)sharedInstance; +@property (nonatomic) NSMutableDictionary* iconCacheKeys; + +// set +- (void)cacheImage:(UIImage*)image forKey:(NSString*)key; +// get +- (UIImage*)getCachedImageForKey:(NSString*)key; +// remove +- (void)removeCachedImageForKey:(NSString*)key; +// remove all +- (void)removeAllCachedImages; +@end + + +#endif diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/UIImageCache.m b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/UIImageCache.m new file mode 100644 index 0000000..e2eb352 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/GoogleMaps/UIImageCache.m @@ -0,0 +1,57 @@ +// +// UIImageCache +// cordova-googlemaps-plugin v2 +// +// Created by Masashi Katsumata. +// +// +#import "UIImageCache.h" + +static UIImageCache *sharedInstance; + +@interface UIImageCache () +@property (nonatomic, strong) NSCache *imageCache; +@end + +@implementation UIImageCache + ++ (UIImageCache*)sharedInstance { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[UIImageCache alloc] init]; + sharedInstance.imageCache = [[NSCache alloc] init]; + sharedInstance.imageCache.totalCostLimit = 3 * 1024 * 1024 * 1024; // 3MB = Cache for image + sharedInstance.iconCacheKeys = [[NSMutableDictionary alloc] init]; + }); + return sharedInstance; +} +- (instancetype)init { + self = [super init]; + /* + if (self && self.imageCache == nil) { + self.imageCache = [[NSCache alloc] init]; + self.imageCache.totalCostLimit = 3 * 1024 * 1024 * 1024; // 3MB = Cache for image + self.iconCacheKeys = [NSMutableDictionary dictionary]; + }*/ + return self; +} + +- (void)cacheImage:(UIImage*)image forKey:(NSString*)key { + NSData *imgData = UIImagePNGRepresentation(image); + [self.imageCache setObject:image forKey:key cost:[imgData length]]; + imgData = nil; +} + +- (UIImage*)getCachedImageForKey:(NSString*)key { + return [self.imageCache objectForKey:key]; +} +- (void)removeCachedImageForKey:(NSString*)key { + [self.imageCache removeObjectForKey:key]; +} + +- (void)removeAllCachedImages { + [self.imageCache removeAllObjects]; +} + + +@end diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/check_sdk_version.js b/node_modules/cordova-plugin-googlemaps/src/ios/check_sdk_version.js new file mode 100644 index 0000000..e527c60 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/check_sdk_version.js @@ -0,0 +1,46 @@ +module.exports = function (ctx) { + + var PluginInfoProvider = require('cordova-common').PluginInfoProvider; + + var path = require('path'); + + var projectRoot = ctx.opts.projectRoot; + return (new Promise(function (resolve, reject) { + + var pluginsDir = path.join(projectRoot, 'plugins'); + var pluginInfoProvider = new PluginInfoProvider(); + var plugins = pluginInfoProvider.getAllWithinSearchPath(pluginsDir); + var pluginInfo; + var needToUninstall = false; + for (var i = 0; i < plugins.length; i++) { + pluginInfo = plugins[i]; + if (pluginInfo.id === 'com.googlemaps.ios') { + needToUninstall = true; + break; + } + } + + if (needToUninstall) { + console.info('--[cordova-plugin-googlemaps]------------------------'); + console.info('From version 2.4.5, the cordova-plugin-googlemaps uses CocoaPod.'); + console.info('No longer necessary com.googlemaps.ios plugin.'); + console.info('Automatic uninstalling com.googlemaps.ios plugin...'); + console.info('-----------------------------------------------------'); + + var exec = require('child_process').exec; + exec('cordova plugin rm com.googlemaps.ios 2>&1', function (err, stdout) { + if (err) { + reject(err); + } else { + console.log(stdout); + exec('npm uninstall cordova-plugin-googlemaps-sdk --save 2>&1', function () { + resolve(); + }); + } + }); + } else { + resolve(); + } + })); + +}; diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ar.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ar.json new file mode 100644 index 0000000..b54a374 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ar.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "إغلاق", + "APIKEY_IS_UNDEFINED_TITLE" : "خطأ في مفتاح الأ بي إي (API)", + "APIKEY_IS_UNDEFINED_MESSAGE" : "ليس هنالك أي قيمة للمتغير \"GOOGLE_MAPS_IOS_API_KEY\" ", + "APP_NAME_ERROR_TITLE" : "يجب عليك تغيير ملف config.xml", + "APP_NAME_ERROR_MESSAGE" : "CordovaGoogleMaps.m لمزيد من المعلومات ، انظر التعليق في \n. عندما تكون الأحرف غير الإنجليزية قيد الاستخدام Google Maps SDK في CFBundleExecutable في حالة تعطل", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "خدمة تحديد المواقع غير متوفرة", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Settings> Privacy> Location Services يحتاج هذا التطبيق إلى تحديد موقعك. يرجى تشغيله من إعدادات الهاتف ", + "LOCATION_IS_DENIED_MESSAGE" : ". رفض هذا التطبيق إستعمال خدمة تحديد المواقع", + "CAN_NOT_GET_LOCATION_MESSAGE" : ". موقعك غير متاح", + "LOCATION_REJECTED_BY_USER_MESSAGE" : ". رفض المستخدم خدمة تحديد المواقع" +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_da.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_da.json new file mode 100644 index 0000000..f00d422 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_da.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "LUK", + "APIKEY_IS_UNDEFINED_TITLE" : "Fejl med API-nøgle", + "APIKEY_IS_UNDEFINED_MESSAGE" : "Proszę zastąpić \"GOOGLE_MAPS_IOS_API_KEY\" w pliku config.xml własnym kluczem API!", + "APP_NAME_ERROR_TITLE" : "Vær sød at ændre config.xml filen", + "APP_NAME_ERROR_MESSAGE" : "Google Maps IOS SDK forårsager fejl, hvis CFBundleExecutable indeholder ikke-engelske tegn.\n Flere detaljer er tilgængelige i kommentaren i filen CordovaGoogleMaps.m.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Placeringstjenester er ikke tilgængelige", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Applikationen kræver adgang til lokationstjenester. Aktivér \"Indstillinger > Privatliv > Placeringstjenester\"", + "LOCATION_IS_DENIED_MESSAGE" : "Applikationen er ikke autoriseret til at bruge lokationstjenester.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Placeringen kan ikke bestemmes.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Placeringstjenester blev afvist af brugeren." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_de.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_de.json new file mode 100644 index 0000000..6cade0d --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_de.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "SCHLIESSEN", + "APIKEY_IS_UNDEFINED_TITLE" : "API Key Fehler", + "APIKEY_IS_UNDEFINED_MESSAGE" : "Die Variable \"GOOGLE_MAPS_IOS_API_KEY\" ist nicht definiert.", + "APP_NAME_ERROR_TITLE" : "Du musst die config.xml Datei ändern.", + "APP_NAME_ERROR_MESSAGE" : "Die Google Maps SDK für iOS stürzt ab, wenn CFBundleExecutable non-Latin Zeichen enthält.\nFür weitere Informationen bitte die CordovaGoogleMaps.m Datei anschauen.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Lokalisierung ist nicht verfügbar.", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Diese App benötigt Zugriff auf deinen Standort. Bitte aktiviere diese unter \"Einstellungen > Datenschutz > Lokalisierung\"", + "LOCATION_IS_DENIED_MESSAGE" : "Dieser App wurde der Zugriff auf die Lokalisierung verweigert.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Standort konnte nicht bestimmt werden.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Standortbestimmung wurde durch den Benutzer verweigert." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_en.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_en.json new file mode 100644 index 0000000..7549870 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_en.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "CLOSE", + "APIKEY_IS_UNDEFINED_TITLE" : "API key error", + "APIKEY_IS_UNDEFINED_MESSAGE" : "The variable \"GOOGLE_MAPS_IOS_API_KEY\" is not registered.", + "APP_NAME_ERROR_TITLE" : "You need to change the config.xml file", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK for iOS crashes if CFBundleExecutable contains non-English characters.\nMore details, Check out the comment in the CordovaGoogleMaps.m file.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Location Service is not available", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "This app needs to access your location. Please turn on \"Settings > Privacy > Location Services\"", + "LOCATION_IS_DENIED_MESSAGE" : "This app has rejected to use Location Services.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Cannot get your location.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Location service is rejected by user." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_es.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_es.json new file mode 100644 index 0000000..8df3fcd --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_es.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "CERRAR", + "APIKEY_IS_UNDEFINED_TITLE" : "Error en API key", + "APIKEY_IS_UNDEFINED_MESSAGE" : "La variable \"GOOGLE_MAPS_IOS_API_KEY\" no está registrada.", + "APP_NAME_ERROR_TITLE" : "Necesitas cambiar el archivo config.xml", + "APP_NAME_ERROR_MESSAGE" : "El SDK de Google Maps para iOS se detiene si CFBundleExecutable contiene caracteres que no pertenecen al inglés. Revisa el comentario en el archivo CordovaGoogleMaps.m", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "El Servicio de Localización no se encuentra disponible", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Esta aplicación necesita acceso a tu localización. Por favor enciéndelo en \"Configuración > Privacidad > Localización\"", + "LOCATION_IS_DENIED_MESSAGE" : "Esta aplicación ha rechazado el uso de los Servicios de Localización.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "No se puede obtener la ubicación.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Servicio de localización rechazado por el usuario." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_fr.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_fr.json new file mode 100644 index 0000000..0f2b31d --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_fr.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "FERMER", + "APIKEY_IS_UNDEFINED_TITLE" : "Erreur liée à la clé API", + "APIKEY_IS_UNDEFINED_MESSAGE" : "Variable \"GOOGLE_MAPS_IOS_API_KEY\" non enregistrée.", + "APP_NAME_ERROR_TITLE" : "Vous devez modifier le fichier config.xml", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK pour iOS crash si CFBundleExecutable contient des caractères non-Anglais.\nPour plus de détails, veuillez consulter le commentaire dans le fichier CordovaGoogleMaps.m.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Service de localisation non disponible", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Cette application nécessite un accès à votre localisation. Veuillez activer le service de localisation dans \"Réglages > Confidentialité > Service de localisation\"", + "LOCATION_IS_DENIED_MESSAGE" : "Cette application a rejeté le service de localisation.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Votre localisation est introuvable.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Le service de localisation a été rejeté par cet utilisateur." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_hi.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_hi.json new file mode 100644 index 0000000..42e06f1 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_hi.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "बंद", + "APIKEY_IS_UNDEFINED_TITLE" : "एपीआई कुंजी त्रुटि", + "APIKEY_IS_UNDEFINED_MESSAGE" : "चर \"GOOGLE_MAPS_IOS_API_KEY\" पंजीकृत (रजिस्टर) नहीं हुआ.", + "APP_NAME_ERROR_TITLE" : "आपको config.xml फ़ाइल को बदलना होगा", + "APP_NAME_ERROR_MESSAGE" : "अगर CFBundleExecutable मे गैर-अंग्रेज़ी वर्णों होने पर गूगल मैप्स एसडीके (Google Maps SDK) क्रैश हो जाता है.\nऔर जानकारी के लिए CordovaGoogleMaps.m मे टिप्पणी को देखें.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "स्थान सेवा उपलब्ध नहीं है", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "इस एप को आप के स्थान की ज़रुरत है. कृपया \"Settings > Privacy > Location Services\" से चालू करें", + "LOCATION_IS_DENIED_MESSAGE" : "इस एप ने स्थान सेवा को उपयोग करना अस्वीकार कर दिया है.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "आपका स्थान उपलब्ध नही है.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "उपयोगकर्ता ने स्थान सेवा अस्वीकार कर दिया है." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_in.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_in.json new file mode 100644 index 0000000..062c955 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_in.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "TUTUP", + "APIKEY_IS_UNDEFINED_TITLE" : "API key Anda Salah", + "APIKEY_IS_UNDEFINED_MESSAGE" : "variable \"GOOGLE_MAPS_IOS_API_KEY\" tidak terdaftar.", + "APP_NAME_ERROR_TITLE" : "anda perlu merubah file config.xml ", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK untuk iOS cras jika CFBundleExecutable ber isi karakter non English.\nLebih detail, silahkan lihat komentar dalam file CordovaGoogleMaps.m", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "tidak terdapat Service Lokasi", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Applikasi ini membutuhkan akses lokasi anda. Silahkan aktifkan \"Pengaturan > Privaci > Lokasi\"", + "LOCATION_IS_DENIED_MESSAGE" : "Applikasi ini tidak dapat menggunakan service Lokasi.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Tidak dapat menemukan lokasi anda.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Service lokasi ditolak oleh Pengguna." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ja.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ja.json new file mode 100644 index 0000000..603c500 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ja.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "閉じる", + "APIKEY_IS_UNDEFINED_TITLE" : "APIキー エラー", + "APIKEY_IS_UNDEFINED_MESSAGE" : "\"GOOGLE_MAPS_IOS_API_KEY\"変数が設定されていません", + "APP_NAME_ERROR_TITLE" : "config.xmlの変更が必要です", + "APP_NAME_ERROR_MESSAGE" : "CFBundleExecutableに英数字以外の文字が含まれていると\nGoogle Maps SDK for iOSがクラッシュします。\n詳しくはCordovaGoogleMaps.mファイルのコメントを\nお読みください。", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "位置情報が有効になっていません", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "この端末の位置情報が無効になっています。\"設定 > プライバシー > 位置情報サービス\"を有効にしてください", + "LOCATION_IS_DENIED_MESSAGE" : "位置情報サービスの利用が拒否されました", + "CAN_NOT_GET_LOCATION_MESSAGE" : "現在位置を取得できませんでした", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "位置情報サービスの利用が拒否されました" +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_nb.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_nb.json new file mode 100644 index 0000000..423d413 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_nb.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "Lukk", + "APIKEY_IS_UNDEFINED_TITLE" : "API-nøkkel feil", + "APIKEY_IS_UNDEFINED_MESSAGE" : "Variabelen \"GOOGLE_MAPS_IOS_API_KEY\" er ikke registrert.", + "APP_NAME_ERROR_TITLE" : "Du må endre config.xml filen", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK til iOS ødelegges hvis CFBundleExecutable inneholder ikke-engelske simboler.\nFlere detaljer, Skjekk ut kommentar i CordovaGoogleMaps.m filen.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Stedtjenester er ikke tilgjengelig", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Denne applikasjon trenger tilghengelighet til din lokasjon. Vennlighst slå på \"Innstellinger > Personvern > Stedtjenester\"", + "LOCATION_IS_DENIED_MESSAGE" : "Denne applikasjon avviser bruk av Stedtjenester.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Kan ikke få din lokasjon.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Stedtjenester ble avvist av brukeren." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_nl.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_nl.json new file mode 100644 index 0000000..a35ca64 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_nl.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "SLUITEN", + "APIKEY_IS_UNDEFINED_TITLE" : "API key fout", + "APIKEY_IS_UNDEFINED_MESSAGE" : "De variabele \"GOOGLE_MAPS_IOS_API_KEY\" is niet geregistreerd.", + "APP_NAME_ERROR_TITLE" : "Je moet het config.xml bestand aanpassen.", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK for iOS faalt indien CFBundleExecutable niet-Engelse karakters bevat.\nVoor meer informatie bekijk het commentaar in het CordovaGoogleMaps.m bestand.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "De locatievoorziening is niet beschikbaar", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Deze app wil jouw locatie kunnen bepalen. Zet dit aan via \"Instellingen > Privacy > Locatievoorzieningen\"", + "LOCATION_IS_DENIED_MESSAGE" : "Deze app heeft de locatievoorziening afgewezen.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Kan je locatie niet bepalen.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "De locatievoorziening is afgewezen door de gebruiker." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_pl.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_pl.json new file mode 100644 index 0000000..a8f600c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_pl.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "ZAMKNIJ", + "APIKEY_IS_UNDEFINED_TITLE" : "Błąd klucza API", + "APIKEY_IS_UNDEFINED_MESSAGE" : "Zmienna \"GOOGLE_MAPS_IOS_API_KEY\" nie została zarejestrowana.", + "APP_NAME_ERROR_TITLE" : "Proszę zmienić plik config.xml", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK dla iOS powoduje błędy, jeżeli CFBundleExecutable zawiera nie angielskie znaki.\nWięcej szczegółów dostępnych w komentarzu w pliku CordovaGoogleMaps.m.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Usługi lokalizacji są niedostępne", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Aplikacja wymaga dostępu do usług lokalizacji. Proszę włączyć \"Ustawienia > Prywatność > Usługi lokalizacji\"", + "LOCATION_IS_DENIED_MESSAGE" : "Aplikacja nie posiada zezwolenia na używanie usług lokalizacji.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Nie można określić położenia.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Usługi lokalizacji zostały odrzucone przez użytkownika." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_pt-BR.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_pt-BR.json new file mode 100644 index 0000000..f791bb4 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_pt-BR.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "FECHAR", + "APIKEY_IS_UNDEFINED_TITLE" : "Erro de chave de API", + "APIKEY_IS_UNDEFINED_MESSAGE" : "A variável \"GOOGLE_MAPS_IOS_API_KEY\" não foi registrada.", + "APP_NAME_ERROR_TITLE" : "Você precisa alterar o arquivo config.xml", + "APP_NAME_ERROR_MESSAGE" : "O Google Maps SDK para iOS falha se o campo CFBundleExecutable conter caracteres não-inglês.\nPara maiores detalhes, cheque o comentário no arquivo CordovaGoogleMaps.m", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "O serviço de localização não esta disponível", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Esse aplicativo precisa acessar sua localização. Por favor ative em \"Ajustes > Privacidade > Serviços de Localização\"", + "LOCATION_IS_DENIED_MESSAGE" : "O aplicativo foi rejeitado para usar os Serviços de Localização.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Não foi possível obter sua localização.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "O serviço de localização foi rejeitado pelo usuário." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ru.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ru.json new file mode 100644 index 0000000..b17bc96 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_ru.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "ЗАКРЫТЬ", + "APIKEY_IS_UNDEFINED_TITLE" : "Неверный API ключ.", + "APIKEY_IS_UNDEFINED_MESSAGE" : "Переменная \"GOOGLE_MAPS_IOS_API_KEY\" не зарегистрирована.", + "APP_NAME_ERROR_TITLE" : "Вам нужно изменить файл config.xml", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK for iOS не работает, если CFBundleExecutable содержит неанглийские символы.\nБолее подробно, в коментариях к файлу CordovaGoogleMaps.m.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Определение местоположения недоступно.", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Это приложение должно получить доступ к вашему местоположению. Пожалуйста, перейдите \"Settings > Privacy > Location Services\"", + "LOCATION_IS_DENIED_MESSAGE" : "Приложение отклонило запрос на получение местоположения.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Не удается получить Ваше местоположение.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Определение местоположения отклонено пользователем." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_uk.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_uk.json new file mode 100644 index 0000000..8f66e39 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_uk.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "ЗАКРИТИ", + "APIKEY_IS_UNDEFINED_TITLE" : "Невірний API ключ", + "APIKEY_IS_UNDEFINED_MESSAGE" : "Змінна \"GOOGLE_MAPS_IOS_API_KEY\" не зареєстрована.", + "APP_NAME_ERROR_TITLE" : "Вам потрібно змінити файл config.xml", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK for iOS не працюватиме, якщо CFBundleExecutable міститиме неанглійські слова.\nБільш дутально, в коментарях до файлу CordovaGoogleMaps.m.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Сервіс пошуку розташування недоступний", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Цей пристрій повинен отримати Ваше місцезнаходження. Будь ласка перейдіть \"Settings > Privacy > Location Services\"", + "LOCATION_IS_DENIED_MESSAGE" : "Сервіс пошуку місцезнаходження відхилений.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Невдається отримати Ваше місцерозташування.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Сервіс пошуку місцезнаходження відхилений користувачем." +} diff --git a/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_vi.json b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_vi.json new file mode 100644 index 0000000..921cc22 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/src/ios/strings/pgm_Localizable_vi.json @@ -0,0 +1,12 @@ +{ + "CLOSE_BUTTON" : "ĐÓNG", + "APIKEY_IS_UNDEFINED_TITLE" : "Lỗi API Key", + "APIKEY_IS_UNDEFINED_MESSAGE" : "\"GOOGLE_MAPS_IOS_API_KEY\" chưa được đăng ký.", + "APP_NAME_ERROR_TITLE" : "Bạn cần cập nhật file config.xml.", + "APP_NAME_ERROR_MESSAGE" : "Google Maps SDK cho iOS sẽ lỗi nếu CFBundleExecutable có chứa các ký tự không phải ký tự tiếng Anh..\nVui lòng kiểm tra bình luận trong file CordovaGoogleMaps.m để có thông tin.", + "LOCATION_IS_UNAVAILABLE_ERROR_TITLE" : "Dịch vụ xác định vị trí không được hỗ trợ", + "LOCATION_IS_UNAVAILABLE_ERROR_MESSAGE" : "Ứng dụng cần xác định vị trí của bạn. Vui lòng bật \"Cấu hình > Quyền riêng tư > Dịch vụ vị trí\"", + "LOCATION_IS_DENIED_MESSAGE" : "Ứng dụng không được cấp quyền sử dụng dịch vụ xác định vị trí.", + "CAN_NOT_GET_LOCATION_MESSAGE" : "Ứng dụng không thể xác định vị trí của bạn.", + "LOCATION_REJECTED_BY_USER_MESSAGE" : "Người dùng không cho phép sử dụng dịch vụ xác định vị trí." + } diff --git a/node_modules/cordova-plugin-googlemaps/tests/package.json b/node_modules/cordova-plugin-googlemaps/tests/package.json new file mode 100644 index 0000000..a9f63f6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/tests/package.json @@ -0,0 +1,14 @@ +{ + "name": "cordova-plugin-googlemaps-tests", + "version": "0.0.3-dev", + "description": "", + "cordova": { + "id": "cordova-plugin-googlemaps-tests", + "platforms": [] + }, + "keywords": [ + "ecosystem:cordova" + ], + "author": "", + "license": "Apache 2.0" +} diff --git a/node_modules/cordova-plugin-googlemaps/tests/plugin.xml b/node_modules/cordova-plugin-googlemaps/tests/plugin.xml new file mode 100644 index 0000000..146c263 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/tests/plugin.xml @@ -0,0 +1,13 @@ + + + Cordova Geolocation Plugin Tests + Apache 2.0 + + + + + diff --git a/node_modules/cordova-plugin-googlemaps/tests/tests.js b/node_modules/cordova-plugin-googlemaps/tests/tests.js new file mode 100644 index 0000000..7629473 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/tests/tests.js @@ -0,0 +1,11 @@ +exports.defineAutoTests = function () { + + describe('FirebaseCore (plugin.firebase.app)', function () { + + it('plugin.firebase.app should exist', function () { + expect(plugin.firebase.app).toBeDefined(); + }); + + }); + +}; diff --git a/node_modules/cordova-plugin-googlemaps/tests/travis.sh b/node_modules/cordova-plugin-googlemaps/tests/travis.sh new file mode 100644 index 0000000..7918e39 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/tests/travis.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -o nounset +set -o errexit + +# run tests appropriate for platform +#if [[ "${CORDOVA_PLATFORM}" == "browser" ]]; then +# npm run test:browser +#fi +if [[ "${CORDOVA_PLATFORM}" == "ios" ]]; then + sudo gem install cocoapods + npm install -g ios-sim ios-deploy + npm run test:ios +fi +echo "CORDOVA_PLATFORM=${CORDOVA_PLATFORM}" +echo "TRAVIS_OS_NAME=${TRAVIS_OS_NAME}" + +if [[ "${CORDOVA_PLATFORM}" == "android" ]]; then + echo "y" | sdkmanager "platforms;android-${ANDROID_API}"; > /dev/null; + echo "y" | android update sdk -a --no-ui --filter android-${ANDROID_API} > /dev/null + echo "y" | android update sdk -a --no-ui --filter sys-img-armeabi-v7a-android-${ANDROID_API} > /dev/null + echo no | android create avd --force -n test -t android-${ANDROID_API} --abi armeabi-v7a > /dev/null + emulator -avd test -no-skin -no-window & + android-wait-for-emulator + adb shell input keyevent 82 & + adb wait-for-device get-serialno + npm run test:android +fi diff --git a/node_modules/cordova-plugin-googlemaps/www/BaseArrayClass.js b/node_modules/cordova-plugin-googlemaps/www/BaseArrayClass.js new file mode 100644 index 0000000..48b5785 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/BaseArrayClass.js @@ -0,0 +1,613 @@ +var utils = require('cordova/utils'), + BaseClass = require('./BaseClass'); + +var ARRAY_FIELD = typeof Symbol === 'undefined' ? '__array' + Date.now() : Symbol('array'); + +var nextTick = function(fn) { Promise.resolve().then(fn); }; +/** + * Create a BaseArrayClass. + * @class + */ +function BaseArrayClass(array) { + BaseClass.apply(this); + var self = this; + self[ARRAY_FIELD] = []; + + if (array && (array instanceof Array || Array.isArray(array))) { + for (var i = 0; i < array.length; i++) { + self[ARRAY_FIELD].push(array[i]); + } + } +} + +utils.extend(BaseArrayClass, BaseClass); + +/** + * The same as `Array.map` but runs a single async operation at a time. + * + * @name mapSeries + * @param {Function} iteratee - An async function to apply to each item in array. + * @param {Function} [callback] - A callback which is called when all `iteratee` functions + * have finished. Results is an array of the transformed items from holding array. + * Invoked with (results); + * @return {Promise} a promise, if no calback if passed. + */ +BaseArrayClass.prototype.mapSeries = function(iteratee, callback) { + if (typeof iteratee !== 'function') { + var error = new Error('iteratee must be a function'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + + var self = this; + + var results = []; + var _arrayLength = self[ARRAY_FIELD].length; + var currentCnt = 0; + if (_arrayLength === 0) { + if (typeof callback === 'function') { + callback.call(self, []); + return; + } else { + return Promise.resolve([]); + } + } + var _looper = function(currentIdx, resolve) { + + iteratee.call(self, self[ARRAY_FIELD][currentIdx], function(value) { + results[currentIdx] = value; + currentCnt++; + if (_arrayLength === currentCnt) { + resolve(results); + } else { + nextTick(function() { + _looper(currentIdx + 1, resolve); + }); + } + }); + }; + + return new Promise(function(resolve) { + nextTick(function() { + _looper(0, resolve); + }); + }).then(function(results) { + if (typeof callback === 'function') { + return callback.call(self, results); + } else { + return Promise.resolve(results); + } + }); +}; + + +/** + * The same as `Array.map` but runs async all `iteratee` function at the same time. + * + * ``` + * baseArray.mapAsync(function(item, idx, callback) { + * ... + * callback(value); + * }).then(function(values) { + * + * }); + * ``` + * + * @name mapAsync + * @param {Function} iteratee - An async function to apply to each item in array. + * @param {Function} [callback] - A callback which is called when all `iteratee` functions + * have finished. Results is an array of the transformed items from holding array. + * Invoked with (results); + * @return {Promise} a promise, if no calback if passed. + */ +BaseArrayClass.prototype.mapAsync = function(iteratee, callback) { + if (typeof iteratee !== 'function') { + var error = new Error('iteratee must be a function'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + var self = this; + var results = []; + for (var i = 0; i < self[ARRAY_FIELD].length; i++) { + results.push(null); + } + var _arrayLength = self[ARRAY_FIELD].length; + var finishCnt = 0; + if (_arrayLength === 0) { + if (typeof callback === 'function') { + callback.call(self, []); + return; + } else { + return Promise.resolve([]); + } + } + + return new Promise(function(resolve) { + for (i = 0; i < self[ARRAY_FIELD].length; i++) { + (function(item, idx) { + nextTick(function() { + iteratee.call(self, item, function(value) { + results[idx] = value; + finishCnt++; + if (finishCnt === _arrayLength) { + resolve(results); + } + }); + }); + })(self[ARRAY_FIELD][i], i); + } + }).then(function(results) { + if (typeof callback === 'function') { + return callback.call(self, results); + } else { + return Promise.resolve(results); + } + }); + +}; + + +/** + * If you provide `iteratee` and `callback`, work as `mapAsync()`. + * If you provide `iteratee`, but omit `callback`, work as `Array.map()`. + * + * @deprecated + * @name map + * @param {Function} iteratee - An async function to apply to each item in array. + * @param {Function} [callback] - A callback which is called when all `iteratee` functions + * have finished. Results is an array of the transformed items from holding array. + * Invoked with (results); + * @return {Promise|any[]} a promise, if no calback if passed. If you omit `callback`, return transformed items. + */ +BaseArrayClass.prototype.map = function(iteratee, callback) { + var self = this; + + if (typeof iteratee !== 'function') { + var error = new Error('iteratee must be a function'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + if (typeof callback !== 'function') { + //------------------------ + // example: + // var values = baseArray.map(function(item, idx) { + // ... + // return someValue; + // }); + //------------------------ + return self[ARRAY_FIELD].map(iteratee.bind(self)); + } + return self.mapAsync(iteratee, callback); +}; + +/** + * The same as `Array.forEach` but runs async all `iteratee` function at the same time. + * + * @name forEachAsync + * @param {Function} iteratee - An async function to apply to each item in array. + * @param {Function} [callback] - A callback which is called when all `iteratee` functions + * have finished. + * @return {Promise} a promise, if no calback if passed. + */ +BaseArrayClass.prototype.forEachAsync = function(iteratee, callback) { + if (typeof iteratee !== 'function') { + var error = new Error('iteratee must be a function'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + var self = this; + //------------------------ + // example: + // baseArray.forEach(function(item, callback) { + // ... + // callback(); + // }, function() { + // + // }); + //------------------------ + var finishCnt = 0; + var _arrayLength = self[ARRAY_FIELD].length; + if (_arrayLength === 0) { + if (typeof callback === 'function') { + callback.call(self); + return; + } else { + return Promise.resolve(); + } + } + + return new Promise(function(resolve) { + + self[ARRAY_FIELD].forEach(function(item) { + iteratee.call(self, item, function() { + finishCnt++; + if (finishCnt === _arrayLength) { + resolve(self); + } + }); + }); + + }) + .then(function() { + if (typeof callback === 'function') { + callback.call(self); + return; + } else { + return Promise.resolve(); + } + }); +}; + +/** + * If you provide `iteratee` and `callback`, work as `forEachAsync()`. + * If you provide `iteratee`, but you omit `callback`, work as `Array.forEach()` + * + * @deprecated + * @name forEach + * @param {Function} iteratee - An async function to apply to each item in array. + * @param {Function} [callback] - A callback which is called when all `iteratee` functions + * have finished. + * Invoked with (results); + * @return {Promise} a promise, if no calback if passed. + */ +BaseArrayClass.prototype.forEach = function(iteratee, callback) { + var self = this; + if (typeof iteratee !== 'function') { + var error = new Error('iteratee must be a function'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + + if (typeof callback !== 'function') { + //------------------------ + // example: + // baseArray.forEach(function(item, idx) { + // ... + // }); + //------------------------ + self[ARRAY_FIELD].forEach(iteratee.bind(self)); + return Promise.resolve(); + } + return self.forEachAsync(iteratee, callback); +}; + + +/** + * The same as `Array.filter` but runs async all `iteratee` function at the same time. + * + * @name filterAsync + * @param {Function} iteratee - An async function to apply to each item in array. + * @param {Function} [callback] - A callback which is called when all `iteratee` functions + * have finished. Results is an array of the filtered items from holding array. + * Invoked with (results); + * @return {Promise} a promise, if no calback if passed. + */ +BaseArrayClass.prototype.filterAsync = function(iteratee, callback) { + var self = this; + if (typeof iteratee !== 'function') { + var error = new Error('iteratee must be a function'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + //------------------------ + // example: + // baseArray.filter(function(item, callback) { + // ... + // callback(true or false); + // }, function(filteredItems) { + // + // }); + //------------------------ + var finishCnt = 0; + var _arrayLength = self[ARRAY_FIELD].length; + if (_arrayLength === 0) { + if (typeof callback === 'function') { + callback.call(self, []); + return; + } else { + return Promise.resolve([]); + } + } + return (new Promise(function(resolve) { + var results = []; + self[ARRAY_FIELD].forEach(function(item, idx) { + iteratee.call(self, item, function(isOk) { + results[idx] = isOk; + finishCnt++; + if (finishCnt === _arrayLength) { + resolve(self[ARRAY_FIELD].filter(function(item, j) { + return results[j]; + })); + } + }); + }); + })) + .then(function(results) { + if (typeof callback === 'function') { + callback.call(self, results); + return; + } else { + return Promise.resolve(results); + } + }); +}; + +/** + * If you provide `iteratee` and `callback`, work as `filterAsync()`. + * If you provide `iteratee`, but you omit `callback`, work as `Array.filter()` + * + * @deprecated + * @name filter + * @param {Function} iteratee - An async function to apply to each item in array. + * @param {Function} [callback] - A callback which is called when all `iteratee` functions + * have finished. + * @return {Promise} a promise, if no calback if passed. + */ +BaseArrayClass.prototype.filter = function(iteratee, callback) { + var self = this; + if (typeof iteratee !== 'function') { + var error = new Error('iteratee must be a function'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + if (typeof callback !== 'function') { + //------------------------ + // example: + // baseArray.filter(function(item, idx) { + // ... + // return true or false + // }); + //------------------------ + return self[ARRAY_FIELD].filter(iteratee); + } + return self.filterAsync(iteratee, callback); +}; + +/** + * Returns the first index at which a given element can be found in the array, or -1 if it is not present. + * + * @name indexOf + * @param {any} searchElement - Element to locate in the array. + * @param {number} [searchElement] - The index to start the search at. + * If the index is greater than or equal to the array's length, -1 is returned, + * which means the array will not be searched. + * If the provided index value is a negative number, + * it is taken as the offset from the end of the array. + * Note: if the provided index is negative, the array is still searched from front to back. + * If the provided index is 0, then the whole array will be searched. Default: 0 (entire array is searched). + * @return The first index of the element in the array; -1 if not found. + */ +BaseArrayClass.prototype.indexOf = function(item, searchElement) { + searchElement = searchElement === undefined || searchElement === null ? 0 : searchElement; + if (typeof searchElement !== 'number') { + throw new Error('searchElement must be a number'); + } + if (searchElement < 0) { + throw new Error('searchElement must be over number than 0'); + } + + return this[ARRAY_FIELD].indexOf(item, searchElement); +}; + +/** + * Removes all elements. Fire `remove_at` event for each element. + * + * @name empty + * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `remove_at` event. + */ +BaseArrayClass.prototype.empty = function(noNotify) { + var self = this; + var cnt = self[ARRAY_FIELD].length; + for (var i = cnt - 1; i >= 0; i--) { + self.removeAt(i, noNotify); + } +}; + +/** + * Adds one element to the end of an array and returns the new length of the array. + * Fire `insert_at` event if `noNotify` is `false`. + * + * @name push + * @param {any} value - The element to add to the end of the array. + * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `insert_at` event. + * @return {number} The new length property of the object upon which the method was called. + */ +BaseArrayClass.prototype.push = function(value, noNotify) { + var self = this; + self[ARRAY_FIELD].push(value); + if (noNotify !== true) { + self.trigger('insert_at', self[ARRAY_FIELD].length - 1); + } + return self[ARRAY_FIELD].length; +}; + +/** + * Adds one element to the end of an array and returns the new length of the array. + * Fire `insert_at` event if `noNotify` is `false`. + * + * @name insertAt + * @param {number} index - The position of the array you want to insert new element. + * @param {any} value - The element to add to the end of the array. + * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `insert_at` event. + * @return {number} The new length property of the object upon which the method was called. + */ +BaseArrayClass.prototype.insertAt = function(index, value, noNotify) { + var self = this; + if (typeof index !== 'number') { + throw new Error('index must be a number'); + } + if (index < 0) { + throw new Error('index must be over number than 0'); + } + self[ARRAY_FIELD].splice(index, 0, value); + if (noNotify !== true) { + self.trigger('insert_at', index); + } + return self[ARRAY_FIELD].length; +}; + +/** + * Returns a new array that is the clone of internal array. + * + * @name getArray + * @return {Array} New array + */ +BaseArrayClass.prototype.getArray = function() { + //return _array.slice(0); <-- Android browser keeps the same instance of original array + return JSON.parse(JSON.stringify(this[ARRAY_FIELD])); +}; + +/** + * Returns item of specified position. + * + * @name getAt + * @param {number} index - The position of the array you want to get. + * @return {any} item + */ +BaseArrayClass.prototype.getAt = function(index) { + var self = this; + if (typeof index !== 'number') { + throw new Error('index must be a number'); + } + if (index < 0) { + throw new Error('index must be over number than 0'); + } + if (index >= self[ARRAY_FIELD].length) { + throw new Error('index must be lower number than ' + self[ARRAY_FIELD].length); + } + return this[ARRAY_FIELD][index]; +}; + +/** + * Replaces item of specified position. + * + * @name setAt + * @param {number} index - The position of the array you want to get. + * @param {any} value - New element + * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `set_at` event. + * @return {any} previous item + */ +BaseArrayClass.prototype.setAt = function(index, value, noNotify) { + var self = this; + if (typeof index !== 'number') { + throw new Error('index must be a number'); + } + if (index < 0) { + throw new Error('index must be over number than 0'); + } + if (index > self[ARRAY_FIELD].length) { + for (var i = self[ARRAY_FIELD].length; i <= index; i++) { + self[ARRAY_FIELD][i] = undefined; + } + } + var prev = self[ARRAY_FIELD][index]; + self[ARRAY_FIELD][index] = value; + if (noNotify !== true) { + self.trigger('set_at', index, prev); + } + return prev; +}; + +/** + * Removes item of specified position. + * + * @name removeAt + * @param {number} index - The position of the array you want to get. + * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `remove_at` event. + * @return {any} removed item + */ +BaseArrayClass.prototype.removeAt = function(index, noNotify) { + var self = this; + if (typeof index !== 'number') { + throw new Error('index must be a number'); + } + if (index < 0) { + throw new Error('index must be over number than 0'); + } + if (index >= self[ARRAY_FIELD].length) { + throw new Error('index must be lower number than ' + self[ARRAY_FIELD].length); + } + var value = self[ARRAY_FIELD][index]; + self[ARRAY_FIELD].splice(index, 1); + if (noNotify !== true) { + self.trigger('remove_at', index, value); + } + return value; +}; + +/** + * Removes item of the last array item. + * + * @name pop + * @param {boolean} [noNotify] - Sets `true` if you don't want to fire `remove_at` event. + * @return {any} removed item + */ +BaseArrayClass.prototype.pop = function(noNotify) { + var self = this; + var index = self[ARRAY_FIELD].length - 1; + var value = self[ARRAY_FIELD].pop(); + if (noNotify !== true) { + self.trigger('remove_at', index, value); + } + return value; +}; + +/** + * Returns the length of array. + * + * @name getLength + * @return {any} Number of items + */ +BaseArrayClass.prototype.getLength = function() { + return parseInt(this[ARRAY_FIELD].length, 10); // In order to prevent manupulating through `length` property, conver to number mandatory using `parseInt`. +}; + +/** + * Reverses an array in place. The first array element becomes the last, and the last array element becomes the first. + * + * @name reverse + */ +BaseArrayClass.prototype.reverse = function() { + this[ARRAY_FIELD].reverse(); +}; + +/** + * The `sort()` method sorts the elements of an array in place and returns the array. + * The same as `array.sort()`. + * + * @name sort + * @param {Function} [compareFunction] - Specifies a function that defines the sort order. + * If omitted, the array is sorted according to each character's Unicode code point value, + * according to the string conversion of each element. + */ +BaseArrayClass.prototype.sort = function(compareFunction) { + if (typeof compareFunction === 'function') { + this[ARRAY_FIELD].sort(compareFunction); + } else { + this[ARRAY_FIELD].sort(); + } +}; + + +module.exports = BaseArrayClass; diff --git a/node_modules/cordova-plugin-googlemaps/www/BaseClass.js b/node_modules/cordova-plugin-googlemaps/www/BaseClass.js new file mode 100644 index 0000000..94c9083 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/BaseClass.js @@ -0,0 +1,225 @@ +var VARS_FIELD = typeof Symbol === 'undefined' ? '__vars' + Date.now() : Symbol('vars'); +var SUBSCRIPTIONS_FIELD = typeof Symbol === 'undefined' ? '__subs' + Date.now() : Symbol('subscriptions'); + +function BaseClass() { + this[VARS_FIELD] = {}; + this[SUBSCRIPTIONS_FIELD] = {}; + this.errorHandler = this.errorHandler.bind(this); + + Object.defineProperty(this, 'hashCode', { + value: Math.floor(Date.now() * Math.random()) + }); +} + +BaseClass.prototype = { + empty: function () { + var vars = this[VARS_FIELD]; + + Object.keys(vars).forEach(function (name) { + vars[name] = null; + delete vars[name]; + }); + }, + + get: function (key) { + return this[VARS_FIELD].hasOwnProperty(key) ? this[VARS_FIELD][key] : undefined; + }, + + set: function (key, value, noNotify) { + if (key === '__pgmId') return; + var prev = this.get(key); + + this[VARS_FIELD][key] = value; + + if (!noNotify && prev !== value) { + this.trigger(key + '_changed', prev, value, key); + } + + return this; + }, + + bindTo: function (key, target, targetKey, noNotify) { + if (key === '__pgmId') return; + targetKey = targetKey || key; + + // If `noNotify` is true, prevent `(targetKey)_changed` event occurrs, + // when bind the value for the first time only. + // (Same behaviour as Google Maps JavaScript v3) + target.set(targetKey, this[VARS_FIELD][key], noNotify); + + this.on(key + '_changed', function (oldValue, value) { + target.set(targetKey, value); + }); + }, + + trigger: function (eventName) { + if (!eventName) { + return this; + } + + if (!this[SUBSCRIPTIONS_FIELD][eventName]) { + return this; + } + + var listenerHashMap = this[SUBSCRIPTIONS_FIELD][eventName]; + var keys = Object.keys(listenerHashMap); + var args = Array.prototype.slice.call(arguments, 1); + var self = this; + + keys.forEach(function (_hashCode) { + if (self[SUBSCRIPTIONS_FIELD] && + self[SUBSCRIPTIONS_FIELD][eventName] && + _hashCode in self[SUBSCRIPTIONS_FIELD][eventName]) { + var info = self[SUBSCRIPTIONS_FIELD][eventName][_hashCode]; + + switch (info.kind) { + case 'on': + info.listener.apply(self, args); + break; + case 'onThrottled': + info.args = args; + if (!info.timer) { + info.timer = setTimeout(function () { + info.listener.apply(this, info.args); + info.timer = null; + }.bind(self), info.interval); + } + break; + } + } + }); + + return this; + }, + + on: function (eventName, listener) { + if (!listener || typeof listener !== 'function') { + throw Error('Listener for on()/addEventListener() method is not a function'); + } + if (!listener._hashCode) { + Object.defineProperty(listener, '_hashCode', { + value: Math.floor(Date.now() * Math.random()), + writable: false, + enumerable: false + }); + } + this[SUBSCRIPTIONS_FIELD][eventName] = this[SUBSCRIPTIONS_FIELD][eventName] || {}; + this[SUBSCRIPTIONS_FIELD][eventName][listener._hashCode] = { + 'kind': 'on', + 'listener': listener + }; + return this; + }, + + onThrottled: function (eventName, listener, interval) { + if (!listener || typeof listener !== 'function') { + throw Error('Listener for on()/addEventListener() method is not a function'); + } + if (typeof interval !== 'number' || interval < 1) { + throw Error('interval argument must be bigger number than 0'); + } + if (!listener._hashCode) { + Object.defineProperty(listener, '_hashCode', { + value: Math.floor(Date.now() * Math.random()), + writable: false, + enumerable: false + }); + } + this[SUBSCRIPTIONS_FIELD][eventName] = this[SUBSCRIPTIONS_FIELD][eventName] || {}; + this[SUBSCRIPTIONS_FIELD][eventName][listener._hashCode] = { + 'kind': 'onThrottled', + 'interval': interval, + 'timer': null, + 'listener': listener + }; + return this; + }, + + off: function (eventName, listener) { + if (!eventName && !listener) { + this[SUBSCRIPTIONS_FIELD] = {}; + return this; + } + + if (eventName && !listener) { + this[SUBSCRIPTIONS_FIELD][eventName] = null; + delete this[SUBSCRIPTIONS_FIELD][eventName]; + } else if (this[SUBSCRIPTIONS_FIELD][eventName] && listener._hashCode) { + delete this[SUBSCRIPTIONS_FIELD][eventName][listener._hashCode]; + } + + return this; + }, + + one: function (eventName, listener) { + if (!listener || typeof listener !== 'function') { + throw Error('Listener for one()/addEventListenerOnce() method is not a function'); + } + + var self = this; + + var callback = function () { + self.off(eventName, arguments.callee); + listener.apply(self, arguments); + callback = undefined; + }; + this.on(eventName, callback); + + return this; + }, + + hasEventListener: function (eventName) { + return (eventName in this[SUBSCRIPTIONS_FIELD] && Object.keys(this[SUBSCRIPTIONS_FIELD][eventName]).length > 0); + }, + + destroy: function () { + this.off(); + this.empty(); + }, + + errorHandler: function (error) { + if (error) { + if (typeof console.error === 'function') { + if (this.__pgmId) { + console.error(this.__pgmId, error); + } else { + console.error(error); + } + } else { + if (this.__pgmId) { + console.log(this.__pgmId, error); + } else { + console.log(error); + } + } + this.trigger('error', error instanceof Error ? error : createError(error)); + } + + return false; + } +}; + +BaseClass.prototype.addEventListener = BaseClass.prototype.on; +BaseClass.prototype.addThrottledEventListener = BaseClass.prototype.onThrottled; +BaseClass.prototype.addEventListenerOnce = BaseClass.prototype.one; +BaseClass.prototype.removeEventListener = BaseClass.prototype.off; + +function createError(message, methodName, args) { + var error = new Error(methodName ? [ + 'Got error with message: "', message, '" ', + 'after calling "', methodName, '"' + ].join('') : message); + + Object.defineProperties(error, { + methodName: { + value: methodName + }, + args: { + value: args + } + }); + + return error; +} + +module.exports = BaseClass; diff --git a/node_modules/cordova-plugin-googlemaps/www/CameraPosition.js b/node_modules/cordova-plugin-googlemaps/www/CameraPosition.js new file mode 100644 index 0000000..251707c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/CameraPosition.js @@ -0,0 +1,20 @@ +/******************************************************************************** + * @name CameraPosition + * @class This class represents new camera position + * @property {LatLng} target The location where you want to show + * @property {Number} [tilt] View angle + * @property {Number} [zoom] Zoom level + * @property {Number} [bearing] Map orientation + * @property {Number} [duration] The duration of animation + *******************************************************************************/ +var CameraPosition = function(params) { + var self = this; + self.zoom = params.zoom; + self.tilt = params.tilt; + self.target = params.target; + self.bearing = params.bearing; + self.hashCode = params.hashCode; + self.duration = params.duration; +}; + +module.exports = CameraPosition; diff --git a/node_modules/cordova-plugin-googlemaps/www/Circle.js b/node_modules/cordova-plugin-googlemaps/www/Circle.js new file mode 100644 index 0000000..0f4c100 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Circle.js @@ -0,0 +1,174 @@ +var utils = require('cordova/utils'), + common = require('./Common'), + LatLngBounds = require('./LatLngBounds'), + Overlay = require('./Overlay'); + +/***************************************************************************** + * Circle Class + *****************************************************************************/ +var Circle = function (map, circleOptions, _exec) { + Overlay.call(this, map, circleOptions, 'Circle', _exec); + + var self = this; + + //----------------------------------------------- + // Sets event listeners + //----------------------------------------------- + self.on('center_changed', function () { + var center = self.get('center'); + center.lat = parseFloat(center.lat, 10); + center.lng = parseFloat(center.lng, 10); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setCenter', [self.getId(), center.lat, center.lng]); + }); + self.on('fillColor_changed', function () { + var color = self.get('fillColor'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setFillColor', [self.getId(), common.HTMLColor2RGBA(color, 0.75)]); + }); + self.on('strokeColor_changed', function () { + var color = self.get('strokeColor'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setStrokeColor', [self.getId(), common.HTMLColor2RGBA(color, 0.75)]); + }); + self.on('strokeWidth_changed', function () { + var width = self.get('strokeWidth'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setStrokeWidth', [self.getId(), width]); + }); + self.on('clickable_changed', function () { + var clickable = self.get('clickable'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setClickable', [self.getId(), clickable]); + }); + self.on('radius_changed', function () { + var radius = self.get('radius'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setRadius', [self.getId(), radius]); + }); + self.on('zIndex_changed', function () { + var zIndex = self.get('zIndex'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setZIndex', [self.getId(), zIndex]); + }); + self.on('visible_changed', function () { + var visible = self.get('visible'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setVisible', [self.getId(), visible]); + }); + +}; + +utils.extend(Circle, Overlay); + +Circle.prototype.getCenter = function () { + return this.get('center'); +}; +Circle.prototype.getRadius = function () { + return this.get('radius'); +}; +Circle.prototype.getStrokeColor = function () { + return this.get('strokeColor'); +}; +Circle.prototype.getStrokeWidth = function () { + return this.get('strokeWidth'); +}; +Circle.prototype.getZIndex = function () { + return this.get('zIndex'); +}; +Circle.prototype.getVisible = function () { + return this.get('visible'); +}; +Circle.prototype.getClickable = function () { + return this.get('clickable'); +}; +Circle.prototype.setCenter = function (center) { + center.lat = parseFloat(center.lat, 10); + center.lng = parseFloat(center.lng, 10); + this.set('center', center); + return this; +}; +Circle.prototype.setFillColor = function (color) { + this.set('fillColor', color); + return this; +}; +Circle.prototype.setStrokeColor = function (color) { + this.set('strokeColor', color); + return this; +}; +Circle.prototype.setStrokeWidth = function (width) { + this.set('strokeWidth', width); + return this; +}; +Circle.prototype.setVisible = function (visible) { + visible = common.parseBoolean(visible); + this.set('visible', visible); + return this; +}; +Circle.prototype.setClickable = function (clickable) { + clickable = common.parseBoolean(clickable); + this.set('clickable', clickable); + return this; +}; +Circle.prototype.setZIndex = function (zIndex) { + this.set('zIndex', zIndex); + return this; +}; +Circle.prototype.setRadius = function (radius) { + this.set('radius', radius); + return this; +}; + +Circle.prototype.remove = function (callback) { + var self = this; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + self.trigger(self.__pgmId + '_remove'); + + var resolver = function(resolve, reject) { + self.exec.call(self, + function() { + self.destroy(); + resolve.call(self); + }, + reject.bind(self), + self.getPluginName(), 'remove', [self.getId()], { + remove: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + +}; + +Circle.prototype.getBounds = function () { + var d2r = Math.PI / 180; // degrees to radians + var r2d = 180 / Math.PI; // radians to degrees + var earthsradius = 3963.189; // 3963 is the radius of the earth in miles + var radius = this.get('radius'); + var center = this.get('center'); + radius *= 0.000621371192; + + // find the raidus in lat/lon + var rlat = (radius / earthsradius) * r2d; + var rlng = rlat / Math.cos(center.lat * d2r); + + var bounds = new LatLngBounds(); + var ex, ey; + for (var i = 0; i < 360; i += 90) { + ey = center.lng + (rlng * Math.cos(i * d2r)); // center a + radius x * cos(theta) + ex = center.lat + (rlat * Math.sin(i * d2r)); // center b + radius y * sin(theta) + bounds.extend({ + lat: ex, + lng: ey + }); + } + return bounds; +}; + +module.exports = Circle; diff --git a/node_modules/cordova-plugin-googlemaps/www/Cluster.js b/node_modules/cordova-plugin-googlemaps/www/Cluster.js new file mode 100644 index 0000000..7c09aa6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Cluster.js @@ -0,0 +1,112 @@ +var LatLngBounds = require('./LatLngBounds'); + +/***************************************************************************** + * Cluster Class + *****************************************************************************/ +var Cluster = function(__pgmId, geocell) { + var obj = {}; + + var self = this; + Object.defineProperty(self, '__pgmId', { + value: __pgmId, + writable: false + }); + Object.defineProperty(self, 'geocell', { + value: geocell, + writable: false + }); + + Object.defineProperty(self, 'type', { + value: 'Cluster', + writable: false + }); + Object.defineProperty(self, '_markerArray', { + value: [], + writable: false + }); + + + Object.defineProperty(self, 'set', { + value: function(key, value) { + obj[key] = value; + }, + writable: false + }); + + Object.defineProperty(self, 'get', { + value: function(key) { + return obj[key]; + }, + writable: false + }); +}; + +Cluster.prototype.NO_CLUSTER_MODE = 1; +Cluster.prototype.CLUSTER_MODE = 2; + +Cluster.prototype.getPluginName = function() { + return this.map.getId() + '-cluster'; +}; +Cluster.prototype.getBounds = function() { + return this.get('bounds'); +}; +/* +Cluster.prototype.getBounds = function() { + var bounds = this.get('bounds'); + if (!bounds) { + bounds = geomodel.computeBox(this.geocell); + this.set('bounds', bounds); + } + return bounds; +}; +*/ +Cluster.prototype.getCenter = function() { + return this.getBounds().getCenter(); +}; + +Cluster.prototype.getMarkers = function() { + return this._markerArray; +}; + +Cluster.prototype.addMarkers = function(markerRefs) { + var self = this; + var bounds = this.get('bounds') || new LatLngBounds(markerRefs[0].get('position'), markerRefs[0].get('position')); + + markerRefs.forEach(function(marker) { + if (self._markerArray.indexOf(marker) === -1) { + marker.get('_cluster').isAdded = true; + self._markerArray.push(marker); + bounds.extend(marker.get('position')); + } + }); + + this.set('bounds', bounds); +}; +Cluster.prototype.getId = function() { + return this.__pgmId; +}; +Cluster.prototype.setMode = function(mode) { + this.set('mode', mode); +}; +Cluster.prototype.getMode = function() { + return this.get('mode'); +}; +Cluster.prototype.removeMarker = function(marker) { + + var idx = this._markerArray.indexOf(marker); + if (idx !== -1) { + this._markerArray.splice(idx, 1); + } +}; + +Cluster.prototype.remove = function() { + this.set('isRemoved', true); + this._markerArray.forEach(function(marker) { + marker.get('_cluster').isAdded = false; + }); +}; +Cluster.prototype.getItemLength = function() { + return this._markerArray.length; +}; + +module.exports = Cluster; diff --git a/node_modules/cordova-plugin-googlemaps/www/Common.js b/node_modules/cordova-plugin-googlemaps/www/Common.js new file mode 100644 index 0000000..cf62018 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Common.js @@ -0,0 +1,796 @@ + +var BaseArrayClass = require('./BaseArrayClass'); +var utils = require('cordova/utils'); + +var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve(); +var nextTick = resolvedPromise ? function (fn) { + resolvedPromise.then(fn); +} : function (fn) { + setTimeout(fn); +}; + +//--------------------------- +// Convert HTML color to RGB +//--------------------------- +function isHTMLColorString(inputValue) { + if (!inputValue || typeof inputValue !== 'string') { + return false; + } + if (inputValue.match(/^#[0-9A-F]{3}$/i) || + inputValue.match(/^#[0-9A-F]{4}$/i) || + inputValue.match(/^#[0-9A-F]{6}$/i) || + inputValue.match(/^#[0-9A-F]{8}$/i) || + inputValue.match(/^rgba?\([\d,.\s]+\)$/) || + inputValue.match(/^hsla?\([\d%,.\s]+\)$/)) { + return true; + } + + inputValue = inputValue.toLowerCase(); + return inputValue in HTML_COLORS; +} + +function HTMLColor2RGBA(colorValue, defaultOpacity) { + defaultOpacity = !defaultOpacity ? 1.0 : defaultOpacity; + if (colorValue instanceof Array) { + return colorValue; + } + if (colorValue === 'transparent' || !colorValue) { + return [0, 0, 0, 0]; + } + var alpha = Math.floor(255 * defaultOpacity), + matches; + var colorStr = colorValue.toLowerCase(); + if (colorStr in HTML_COLORS) { + colorStr = HTML_COLORS[colorStr]; + } + if (colorStr.match(/^#([0-9A-F]){3}$/i)) { + matches = colorStr.match(/([0-9A-F])/ig); + + return [ + parseInt(matches[0], 16), + parseInt(matches[1], 16), + parseInt(matches[2], 16), + alpha + ]; + } + + if (colorStr.match(/^#[0-9A-F]{4}$/i)) { + alpha = colorStr.substr(4, 1); + alpha = parseInt(alpha + alpha, 16); + + matches = colorStr.match(/([0-9A-F])/ig); + return [ + parseInt(matches[0], 16), + parseInt(matches[1], 16), + parseInt(matches[2], 16), + alpha + ]; + } + + if (colorStr.match(/^#[0-9A-F]{6}$/i)) { + matches = colorStr.match(/([0-9A-F]{2})/ig); + return [ + parseInt(matches[0], 16), + parseInt(matches[1], 16), + parseInt(matches[2], 16), + alpha + ]; + } + if (colorStr.match(/^#[0-9A-F]{8}$/i)) { + matches = colorStr.match(/([0-9A-F]{2})/ig); + + return [ + parseInt(matches[0], 16), + parseInt(matches[1], 16), + parseInt(matches[2], 16), + parseInt(matches[3], 16) + ]; + } + // convert rgb(), rgba() + if (colorStr.match(/^rgba?\([\d,.\s]+\)$/)) { + matches = colorStr.match(/([\d.]+)/g); + alpha = matches.length == 4 ? Math.floor(parseFloat(matches[3]) * 255) : alpha; + return [ + parseInt(matches[0], 10), + parseInt(matches[1], 10), + parseInt(matches[2], 10), + alpha + ]; + } + + // convert hsl(), hsla() + if (colorStr.match(/^hsla?\([\d%,.\s]+\)$/)) { + matches = colorStr.match(/([\d%.]+)/g); + alpha = matches.length == 4 ? Math.floor(parseFloat(matches[3]) * 255) : alpha; + var rgb = HLStoRGB(matches[0], matches[1], matches[2]); + rgb.push(alpha); + return rgb; + } + + console.warn('Warning: "' + colorValue + '" is not available. The overlay is drew by black.'); + return [0, 0, 0, alpha]; +} + +/** + * http://d.hatena.ne.jp/ja9/20100907/1283840213 + */ +function HLStoRGB(h, l, s) { + var r, g, b; // 0..255 + + while (h < 0) { + h += 360; + } + h = h % 360; + + // In case of saturation = 0 + if (s === 0) { + // RGB are the same as V + l = Math.round(l * 255); + return [l, l, l]; + } + + var m2 = (l < 0.5) ? l * (1 + s) : l + s - l * s, + m1 = l * 2 - m2, + tmp; + + tmp = h + 120; + if (tmp > 360) { + tmp = tmp - 360; + } + + if (tmp < 60) { + r = (m1 + (m2 - m1) * tmp / 60); + } else if (tmp < 180) { + r = m2; + } else if (tmp < 240) { + r = m1 + (m2 - m1) * (240 - tmp) / 60; + } else { + r = m1; + } + + tmp = h; + if (tmp < 60) { + g = m1 + (m2 - m1) * tmp / 60; + } else if (tmp < 180) { + g = m2; + } else if (tmp < 240) { + g = m1 + (m2 - m1) * (240 - tmp) / 60; + } else { + g = m1; + } + + tmp = h - 120; + if (tmp < 0) { + tmp = tmp + 360; + } + if (tmp < 60) { + b = m1 + (m2 - m1) * tmp / 60; + } else if (tmp < 180) { + b = m2; + } else if (tmp < 240) { + b = m1 + (m2 - m1) * (240 - tmp) / 60; + } else { + b = m1; + } + return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; +} + +function parseBoolean(boolValue) { + return typeof (boolValue) === 'string' && boolValue.toLowerCase() === 'true' || + boolValue === true || + boolValue === 1; +} + +function isDom(element) { + return element && + element.nodeType === Node.ELEMENT_NODE && + element instanceof SVGElement === false && + typeof element.getBoundingClientRect === 'function'; +} + +function getDivRect(div) { + if (!div) { + return; + } + var rect; + if (div === document.body) { + rect = div.getBoundingClientRect(); + rect.left = Math.max(rect.left, window.pageOffsetX); + rect.top = Math.max(rect.top, window.pageOffsetY); + rect.width = Math.max(rect.width, window.innerWidth); + rect.height = Math.max(rect.height, window.innerHeight); + rect.right = rect.left + rect.width; + rect.bottom = rect.top + rect.height; + } else { + rect = div.getBoundingClientRect(); + if ('right' in rect === false) { + rect.right = rect.left + rect.width; + } + if ('bottom' in rect === false) { + rect.bottom = rect.top + rect.height; + } + } + return { + left: rect.left, + top: rect.top, + width: rect.width, + height: rect.height, + right: rect.right, + bottom: rect.bottom + }; +} + +var ignoreTags = [ + 'pre', 'textarea', 'p', 'input', 'caption', 'canvas', 'svg' +]; + +function shouldWatchByNative(node) { + if (!node || node.nodeType !== Node.ELEMENT_NODE || !node.parentNode || node instanceof SVGElement) { + if (node === document.body) { + return true; + } + return false; + } + + var tagName = node.tagName.toLowerCase(); + if (ignoreTags.indexOf(tagName) > -1) { + return false; + } + + var classNames = (node.className || '').split(' '); + if (classNames.indexOf('_gmaps_cdv_') > -1) { + return true; + } + + var visibilityCSS = getStyle(node, 'visibility'); + var displayCSS = getStyle(node, 'display'); + + // Do not check this at here. + //var pointerEventsCSS = getStyle(node, 'pointer-events'); + + //----------------------------------------- + // no longer check the opacity property, + // because the app might start changing the opacity later. + //----------------------------------------- + //var opacityCSS = getStyle(node, 'opacity'); + //opacityCSS = /^[\d.]+$/.test(opacityCSS + '') ? opacityCSS : 1; + + //----------------------------------------- + // no longer check the clickable size, + // because HTML still can display larger element inside one small element. + //----------------------------------------- + // var clickableSize = ( + // node.offsetHeight > 0 && node.offsetWidth > 0 || + // node.clientHeight > 0 && node.clientWidth > 0); + return displayCSS !== 'none' && + visibilityCSS !== 'hidden'; +} + +// Get z-index order +// http://stackoverflow.com/a/24136505 +var internalCache = {}; + +function _clearInternalCache() { + internalCache = undefined; + internalCache = {}; +} + +function _removeCacheById(elemId) { + delete internalCache[elemId]; +} + +function getZIndex(dom) { + if (dom === document.body) { + internalCache = undefined; + internalCache = {}; + } + if (!dom) { + return 0; + } + + var z = 0; + if (window.getComputedStyle) { + z = document.defaultView.getComputedStyle(dom, null).getPropertyValue('z-index'); + } + if (dom.currentStyle) { + z = dom.currentStyle['z-index']; + } + var elemId = dom.getAttribute('__pluginDomId'); + var parentNode = dom.parentNode; + var parentZIndex = 0; + if (parentNode && parentNode.nodeType === Node.ELEMENT_NODE) { + var parentElemId = parentNode.getAttribute('__pluginDomId'); + if (parentElemId in internalCache) { + parentZIndex = internalCache[parentElemId]; + } else { + parentZIndex = getZIndex(dom.parentNode); + internalCache[parentElemId] = parentZIndex; + } + } + + var isInherit = false; + if (z === 'unset' || z === 'initial') { + z = 0; + } else if (z === 'auto' || z === 'inherit') { + z = 0; + isInherit = true; + } else { + z = parseInt(z); + } + //dom.setAttribute('__ZIndex', z); + internalCache[elemId] = z + parentZIndex; + return { + isInherit: isInherit, + z: z + }; +} + +// Get CSS value of an element +// http://stackoverflow.com/a/1388022 +function getStyle(element, styleProperty) { + if (window.getComputedStyle) { + return document.defaultView.getComputedStyle(element, null).getPropertyValue(styleProperty); + } else if (element.currentStyle) { + return element.currentStyle[styleProperty]; + } + return; +} + +var HTML_COLORS = { + 'aliceblue': '#f0f8ff', + 'antiquewhite': '#faebd7', + 'aqua': '#00ffff', + 'aquamarine': '#7fffd4', + 'azure': '#f0ffff', + 'beige': '#f5f5dc', + 'bisque': '#ffe4c4', + 'black': '#000000', + 'blanchedalmond': '#ffebcd', + 'blue': '#0000ff', + 'blueviolet': '#8a2be2', + 'brown': '#a52a2a', + 'burlywood': '#deb887', + 'cadetblue': '#5f9ea0', + 'chartreuse': '#7fff00', + 'chocolate': '#d2691e', + 'coral': '#ff7f50', + 'cornflowerblue': '#6495ed', + 'cornsilk': '#fff8dc', + 'crimson': '#dc143c', + 'cyan': '#00ffff', + 'darkblue': '#00008b', + 'darkcyan': '#008b8b', + 'darkgoldenrod': '#b8860b', + 'darkgray': '#a9a9a9', + 'darkgrey': '#a9a9a9', + 'darkgreen': '#006400', + 'darkkhaki': '#bdb76b', + 'darkmagenta': '#8b008b', + 'darkolivegreen': '#556b2f', + 'darkorange': '#ff8c00', + 'darkorchid': '#9932cc', + 'darkred': '#8b0000', + 'darksalmon': '#e9967a', + 'darkseagreen': '#8fbc8f', + 'darkslateblue': '#483d8b', + 'darkslategray': '#2f4f4f', + 'darkslategrey': '#2f4f4f', + 'darkturquoise': '#00ced1', + 'darkviolet': '#9400d3', + 'deeppink': '#ff1493', + 'deepskyblue': '#00bfff', + 'dimgray': '#696969', + 'dimgrey': '#696969', + 'dodgerblue': '#1e90ff', + 'firebrick': '#b22222', + 'floralwhite': '#fffaf0', + 'forestgreen': '#228b22', + 'fuchsia': '#ff00ff', + 'gainsboro': '#dcdcdc', + 'ghostwhite': '#f8f8ff', + 'gold': '#ffd700', + 'goldenrod': '#daa520', + 'gray': '#808080', + 'grey': '#808080', + 'green': '#008000', + 'greenyellow': '#adff2f', + 'honeydew': '#f0fff0', + 'hotpink': '#ff69b4', + 'indianred ': '#cd5c5c', + 'indigo ': '#4b0082', + 'ivory': '#fffff0', + 'khaki': '#f0e68c', + 'lavender': '#e6e6fa', + 'lavenderblush': '#fff0f5', + 'lawngreen': '#7cfc00', + 'lemonchiffon': '#fffacd', + 'lightblue': '#add8e6', + 'lightcoral': '#f08080', + 'lightcyan': '#e0ffff', + 'lightgoldenrodyellow': '#fafad2', + 'lightgray': '#d3d3d3', + 'lightgrey': '#d3d3d3', + 'lightgreen': '#90ee90', + 'lightpink': '#ffb6c1', + 'lightsalmon': '#ffa07a', + 'lightseagreen': '#20b2aa', + 'lightskyblue': '#87cefa', + 'lightslategray': '#778899', + 'lightslategrey': '#778899', + 'lightsteelblue': '#b0c4de', + 'lightyellow': '#ffffe0', + 'lime': '#00ff00', + 'limegreen': '#32cd32', + 'linen': '#faf0e6', + 'magenta': '#ff00ff', + 'maroon': '#800000', + 'mediumaquamarine': '#66cdaa', + 'mediumblue': '#0000cd', + 'mediumorchid': '#ba55d3', + 'mediumpurple': '#9370db', + 'mediumseagreen': '#3cb371', + 'mediumslateblue': '#7b68ee', + 'mediumspringgreen': '#00fa9a', + 'mediumturquoise': '#48d1cc', + 'mediumvioletred': '#c71585', + 'midnightblue': '#191970', + 'mintcream': '#f5fffa', + 'mistyrose': '#ffe4e1', + 'moccasin': '#ffe4b5', + 'navajowhite': '#ffdead', + 'navy': '#000080', + 'oldlace': '#fdf5e6', + 'olive': '#808000', + 'olivedrab': '#6b8e23', + 'orange': '#ffa500', + 'orangered': '#ff4500', + 'orchid': '#da70d6', + 'palegoldenrod': '#eee8aa', + 'palegreen': '#98fb98', + 'paleturquoise': '#afeeee', + 'palevioletred': '#db7093', + 'papayawhip': '#ffefd5', + 'peachpuff': '#ffdab9', + 'peru': '#cd853f', + 'pink': '#ffc0cb', + 'plum': '#dda0dd', + 'powderblue': '#b0e0e6', + 'purple': '#800080', + 'rebeccapurple': '#663399', + 'red': '#ff0000', + 'rosybrown': '#bc8f8f', + 'royalblue': '#4169e1', + 'saddlebrown': '#8b4513', + 'salmon': '#fa8072', + 'sandybrown': '#f4a460', + 'seagreen': '#2e8b57', + 'seashell': '#fff5ee', + 'sienna': '#a0522d', + 'silver': '#c0c0c0', + 'skyblue': '#87ceeb', + 'slateblue': '#6a5acd', + 'slategray': '#708090', + 'slategrey': '#708090', + 'snow': '#fffafa', + 'springgreen': '#00ff7f', + 'steelblue': '#4682b4', + 'tan': '#d2b48c', + 'teal': '#008080', + 'thistle': '#d8bfd8', + 'tomato': '#ff6347', + 'turquoise': '#40e0d0', + 'violet': '#ee82ee', + 'wheat': '#f5deb3', + 'white': '#ffffff', + 'whitesmoke': '#f5f5f5', + 'yellow': '#ffff00', + 'yellowgreen': '#9acd32' +}; + +function defaultTrueOption(value) { + return value === undefined ? true : value === true; +} + +function createMvcArray(array) { + if (!array) { + return new BaseArrayClass(); + } + if (array.type === 'BaseArrayClass') { + return array; + } + + var mvcArray; + if (array.type === 'LatLngBounds') { + array = [ + array.southwest, + { + lat: array.northeast.lat, + lng: array.southwest.lng + }, + array.northeast, + { + lat: array.southwest.lat, + lng: array.northeast.lng + }, + array.southwest + ]; + array = array.map(getLatLng); + } + + if (array && typeof array.getArray === 'function') { + mvcArray = new BaseArrayClass(array.getArray()); + array.on('set_at', function (index) { + var value = array.getAt(index); + value = 'position' in value ? value.getPosition() : value; + mvcArray.setAt(index, value); + }); + array.on('insert_at', function (index) { + var value = array.getAt(index); + value = 'position' in value ? value.getPosition() : value; + mvcArray.insertAt(index, value); + }); + array.on('remove_at', function (index) { + mvcArray.removeAt(index); + }); + + } else { + mvcArray = new BaseArrayClass(array ? array.slice(0) : undefined); + } + return mvcArray; +} + +function getLatLng(target) { + return 'getPosition' in target ? target.getPosition() : { + 'lat': parseFloat(target.lat, 10), + 'lng': parseFloat(target.lng, 10) + }; +} + +function convertToPositionArray(array) { + array = array || []; + + if (!utils.isArray(array)) { + if (array.type === 'LatLngBounds') { + array = [ + array.southwest, + { + lat: array.northeast.lat, + lng: array.southwest.lng + }, + array.northeast, + { + lat: array.southwest.lat, + lng: array.northeast.lng + }, + array.southwest + ]; + } else if (array && typeof array.getArray === 'function') { + array = array.getArray(); + } else { + array = [array]; + } + } + + array = array.map(getLatLng); + + return array; +} + +function markerOptionsFilter(markerOptions) { + markerOptions = markerOptions || {}; + + markerOptions.animation = markerOptions.animation || undefined; + markerOptions.position = markerOptions.position || {}; + markerOptions.position.lat = markerOptions.position.lat || 0.0; + markerOptions.position.lng = markerOptions.position.lng || 0.0; + markerOptions.draggable = markerOptions.draggable === true; + markerOptions.icon = markerOptions.icon || undefined; + markerOptions.zIndex = markerOptions.zIndex || 0; + markerOptions.snippet = typeof (markerOptions.snippet) === 'string' ? markerOptions.snippet : undefined; + markerOptions.title = typeof (markerOptions.title) === 'string' ? markerOptions.title : undefined; + markerOptions.visible = defaultTrueOption(markerOptions.visible); + markerOptions.flat = markerOptions.flat === true; + markerOptions.rotation = markerOptions.rotation || 0; + markerOptions.opacity = markerOptions.opacity === 0 ? 0 : (parseFloat('' + markerOptions.opacity, 10) || 1); + markerOptions.disableAutoPan = markerOptions.disableAutoPan === true; + markerOptions.noCache = markerOptions.noCache === true; //experimental + if (typeof markerOptions.icon === 'object') { + if ('anchor' in markerOptions.icon && + !Array.isArray(markerOptions.icon.anchor) && + 'x' in markerOptions.icon.anchor && + 'y' in markerOptions.icon.anchor) { + markerOptions.icon.anchor = [markerOptions.icon.anchor.x, markerOptions.icon.anchor.y]; + } + } + + if ('infoWindowAnchor' in markerOptions && + !Array.isArray(markerOptions.infoWindowAnchor) && + 'x' in markerOptions.infoWindowAnchor && + 'y' in markerOptions.infoWindowAnchor) { + markerOptions.infoWindowAnchor = [markerOptions.infoWindowAnchor.x, markerOptions.infoWindowAnchor.anchor.y]; + } + + if ('style' in markerOptions && !('styles' in markerOptions)) { + markerOptions.styles = markerOptions.style; + delete markerOptions.style; + } + if ('styles' in markerOptions) { + markerOptions.styles = typeof markerOptions.styles === 'object' ? markerOptions.styles : {}; + + if ('color' in markerOptions.styles) { + markerOptions.styles.color = HTMLColor2RGBA(markerOptions.styles.color || '#000000'); + } + } + if (markerOptions.icon && isHTMLColorString(markerOptions.icon)) { + markerOptions.icon = HTMLColor2RGBA(markerOptions.icon); + } + if (markerOptions.icon && markerOptions.icon.label && + isHTMLColorString(markerOptions.icon.label.color)) { + markerOptions.icon.label.color = HTMLColor2RGBA(markerOptions.icon.label.color); + } + return markerOptions; +} + +function quickfilter(domPositions, mapElemIDs) { + //console.log('before', JSON.parse(JSON.stringify(domPositions))); + var keys = Object.keys(domPositions); + + var tree = {}; + mapElemIDs.forEach(function (mapElemId) { + var size = domPositions[mapElemId].size; + var mapRect = { + left: size.left, + top: size.top, + right: size.left + size.width, + bottom: size.top + size.height + }; + + tree[mapElemId] = domPositions[mapElemId]; + + keys.forEach(function (elemId) { + if (domPositions[elemId].ignore) { + return; + } + var domSize = { + left: domPositions[elemId].size.left, + top: domPositions[elemId].size.top, + right: domPositions[elemId].size.left + domPositions[elemId].size.width, + bottom: domPositions[elemId].size.bottom + domPositions[elemId].size.height + }; + if ( + (domSize.left >= mapRect.left && domSize.left <= mapRect.right) || + (domSize.right >= mapRect.left && domSize.right <= mapRect.right) || + (domSize.top >= mapRect.top && domSize.top <= mapRect.bottom) || + (domSize.bottom >= mapRect.top && domSize.bottom <= mapRect.bottom) + ) { + tree[elemId] = domPositions[elemId]; + } + }); + }); + + //console.log('after', JSON.parse(JSON.stringify(tree))); + return tree; +} + +function getPluginDomId(element) { + // Generates a __pluginDomId + if (!element || !shouldWatchByNative(element)) { + return; + } + var elemId = element.getAttribute('__pluginDomId'); + if (!elemId) { + if (element === document.body) { + elemId = 'root'; + } else { + elemId = 'pgm' + Math.floor(Math.random() * Date.now()); + } + element.setAttribute('__pluginDomId', elemId); + } + return elemId; +} + +// Add hashCode() method +// https://stackoverflow.com/a/7616484/697856 +function hashCode(text) { + var hash = 0, + i, chr; + if (text.length === 0) return hash; + for (i = 0; i < text.length; i++) { + chr = text.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +} + +function createEvent(eventName, properties) { + var evt; + if (typeof CustomEvent === 'function') { + evt = new CustomEvent(eventName, { + bubbles: true, + detail: properties || null + }); + } else { + evt = document.createEvent('Event'); + evt.initEvent(eventName, true, false); + if (properties) { + Object.keys(properties).forEach(function (key) { + if (!(key in properties)) { + evt[key] = properties[key]; + } + }); + } + } + return evt; +} + +function attachTransparentClass(div) { + + if (div.classList && !div.classList.contains('_gmaps_cdv_')) { + div.classList.add('_gmaps_cdv_'); + } else if (div.className && div.className.indexOf('_gmaps_cdv_') === -1) { + div.className = div.className + ' _gmaps_cdv_'; + } + + if (div.shadowRoot) { + var styleAttr = div.getAttribute('style') || ''; + if (styleAttr && styleAttr.indexOf('--ion-background-color') === -1) { + styleAttr = styleAttr + ' --ion-background-color: transparent;'; + } + div.setAttribute('style', styleAttr); + } +} +// function dettachTransparentClass(root) { +// +// if (div.classList && div.classList.contains('_gmaps_cdv_')) { +// div.classList.remove('_gmaps_cdv_'); +// } else if (div.className && div.className.indexOf('_gmaps_cdv_') === -1) { +// div.className = div.className.replace('_gmaps_cdv_', ''); +// } +// +// var visibilityCSS = getStyle(node, 'visibility'); +// if (getStyle(div, 'background-color') === 'transparent !important') { +// div.style.backgroundColor = undefined; +// } +// if (getStyle(div, 'background') === 'transparent !important') { +// div.style.backgroundColor = undefined; +// } +// if (getStyle(div, 'background-image') === 'transparent !important') { +// div.style.backgroundImage = undefined; +// } +// if (div.shadowRoot) { +// var hiddenChildren = div.querySelectorAll('*'); +// hiddenChildren = Array.prototype.splice(hiddenChildren, 0); +// hiddenChildren.forEach(dettachTransparentClass); +// } +// } + +module.exports = { + _clearInternalCache: _clearInternalCache, + _removeCacheById: _removeCacheById, + getZIndex: getZIndex, + getDivRect: getDivRect, + isDom: isDom, + parseBoolean: parseBoolean, + HLStoRGB: HLStoRGB, + HTMLColor2RGBA: HTMLColor2RGBA, + isHTMLColorString: isHTMLColorString, + defaultTrueOption: defaultTrueOption, + createMvcArray: createMvcArray, + getStyle: getStyle, + convertToPositionArray: convertToPositionArray, + getLatLng: getLatLng, + shouldWatchByNative: shouldWatchByNative, + markerOptionsFilter: markerOptionsFilter, + quickfilter: quickfilter, + nextTick: nextTick, + getPluginDomId: getPluginDomId, + hashCode: hashCode, + createEvent: createEvent, + //dettachTransparentClass: dettachTransparentClass, + attachTransparentClass: attachTransparentClass +}; + +if (cordova && cordova.platformId === 'browser') { + require('cordova/exec/proxy').add('common', module.exports); +} diff --git a/node_modules/cordova-plugin-googlemaps/www/Environment.js b/node_modules/cordova-plugin-googlemaps/www/Environment.js new file mode 100644 index 0000000..059bb43 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Environment.js @@ -0,0 +1,36 @@ +var common = require('./Common'); + +/***************************************************************************** + * Config Class + *****************************************************************************/ +var Environment = {}; + +Environment.setBackgroundColor = function (color) { + cordova.exec(null, null, 'PluginEnvironment', 'setBackGroundColor', [common.HTMLColor2RGBA(color)]); +}; + +Environment.isAvailable = function (callback) { + cordova.exec(function () { + if (typeof callback === 'function') { + callback(true); + } + }, function (message) { + if (typeof callback === 'function') { + callback(false, message); + } + }, 'PluginEnvironment', 'isAvailable', ['']); +}; + +Environment.getLicenseInfo = function (callback) { + cordova.exec(function (txt) { + callback(txt); + }, null, 'PluginEnvironment', 'getLicenseInfo', []); +}; + +Environment.setEnv = function (options) { + if (options) { + cordova.exec(null, null, 'PluginEnvironment', 'setEnv', [options]); + } +}; + +module.exports = Environment; diff --git a/node_modules/cordova-plugin-googlemaps/www/Geocoder.js b/node_modules/cordova-plugin-googlemaps/www/Geocoder.js new file mode 100644 index 0000000..c583130 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Geocoder.js @@ -0,0 +1,124 @@ + + +/***************************************************************************** + * Geocoder class + *****************************************************************************/ +var common = require('./Common'), + utils = require('cordova/utils'); + +var Geocoder = function (exec) { + + return { + geocode: function (geocoderRequest, callback) { + + geocoderRequest = geocoderRequest || {}; + var requestProperty = null; + if ('position' in geocoderRequest && !('address' in geocoderRequest)) { + requestProperty = 'position'; + } else if ('address' in geocoderRequest && !('position' in geocoderRequest)) { + requestProperty = 'address'; + } + if (!requestProperty) { + var error = new Error('Invalid request'); + if (typeof callback === 'function') { + callback([], error); + } else { + return Promise.reject(error); + } + } + + if (geocoderRequest[requestProperty] instanceof Array || Array.isArray(geocoderRequest[requestProperty])) { + //------------------------- + // Geocoder.geocode({ + // address: [ + // 'Kyoto, Japan', + // 'Tokyo, Japan' + // ] + // }) + //------------------------- + + var mvcResults = common.createMvcArray(); + for (var i = 0; i < requestCnt; i++) { + mvcResults.push(-1, true); + } + + // Execute geocoder.geocode() when a new request is instearted. + var requestCnt = geocoderRequest[requestProperty].length; + var requests = common.createMvcArray(); + requests.on('insert_at', function (idx) { + var request = requests.getAt(idx); + request.idx = idx; + exec.call({ + _isReady: true + }, function (result) { + + mvcResults.insertAt(result.idx, result.results); + if (mvcResults.getLength() === requestCnt) { + var tmp = mvcResults.getArray(); + var tmp2 = tmp.filter(function (ele) { + return ele === -1; + }); + if (tmp2.length === 0) { + // Notifies `finish` event when all results are received. + mvcResults.trigger('finish'); + } + } + + }, function (error) { + mvcResults.trigger('error', error); + }, 'PluginGeocoder', 'geocode', [request]); + }); + + var request; + var baseRequest = utils.clone(geocoderRequest); + delete baseRequest[requestProperty]; + for (i = 0; i < requestCnt; i++) { + request = utils.clone(baseRequest); + request[requestProperty] = geocoderRequest[requestProperty][i]; + request.idx = i; + if (requestProperty === 'position') { + request.position.lat = request.position.lat || 0.0; + request.position.lng = request.position.lng || 0.0; + } + requests.push(request); + } + if (typeof callback === 'function') { + callback(mvcResults); + } else { + return Promise.resolve(mvcResults); + } + } else { + //------------------------- + // Geocoder.geocode({ + // address: 'Kyoto, Japan' + // }) + //------------------------- + if (requestProperty === 'position') { + geocoderRequest.position.lat = geocoderRequest.position.lat || 0.0; + geocoderRequest.position.lng = geocoderRequest.position.lng || 0.0; + } + geocoderRequest.idx = -1; + + var resolver1 = function (resolve, reject) { + exec.call({ + _isReady: true + }, function (_results) { + resolve(_results.results); + }, reject, 'PluginGeocoder', 'geocode', [geocoderRequest]); + }; + + if (typeof callback === 'function') { + resolver1(callback, function (error) { + callback([], error); + }); + } else { + return new Promise(resolver1); + } + + } + } + + }; +}; + +module.exports = Geocoder; diff --git a/node_modules/cordova-plugin-googlemaps/www/GroundOverlay.js b/node_modules/cordova-plugin-googlemaps/www/GroundOverlay.js new file mode 100644 index 0000000..fc6abbb --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/GroundOverlay.js @@ -0,0 +1,152 @@ +var utils = require('cordova/utils'), + common = require('./Common'), + Overlay = require('./Overlay'); + +/***************************************************************************** + * GroundOverlay Class + *****************************************************************************/ +var GroundOverlay = function (map, groundOverlayOptions, _exec) { + Overlay.call(this, map, groundOverlayOptions, 'GroundOverlay', _exec); + + var self = this; + groundOverlayOptions.visible = groundOverlayOptions.visible === undefined ? true : groundOverlayOptions.visible; + groundOverlayOptions.zIndex = groundOverlayOptions.zIndex || 1; + groundOverlayOptions.opacity = groundOverlayOptions.opacity || 1; + groundOverlayOptions.bounds = groundOverlayOptions.bounds || []; + groundOverlayOptions.anchor = groundOverlayOptions.anchor || [0, 0]; + groundOverlayOptions.bearing = groundOverlayOptions.bearing || 0; + + //----------------------------------------------- + // Sets event listeners + //----------------------------------------------- + self.on('visible_changed', function () { + var visible = self.get('visible'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setVisible', [self.getId(), visible]); + }); + self.on('image_changed', function () { + var image = self.get('image'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setImage', [self.getId(), image]); + }); + self.on('bounds_changed', function () { + var bounds = self.get('bounds'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setBounds', [self.getId(), bounds]); + }); + self.on('opacity_changed', function () { + var opacity = self.get('opacity'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setOpacity', [self.getId(), opacity]); + }); + self.on('clickable_changed', function () { + var clickable = self.get('clickable'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setClickable', [self.getId(), clickable]); + }); + self.on('bearing_changed', function () { + var bearing = self.get('bearing'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setBearing', [self.getId(), bearing]); + }); + self.on('zIndex_changed', function () { + var zIndex = self.get('zIndex'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setZIndex', [self.getId(), zIndex]); + }); + +}; + +utils.extend(GroundOverlay, Overlay); + +GroundOverlay.prototype.setVisible = function (visible) { + this.set('visible', visible); +}; + +GroundOverlay.prototype.getVisible = function () { + return this.get('visible'); +}; + +GroundOverlay.prototype.setImage = function (url) { + this.set('image', url); +}; + +GroundOverlay.prototype.setBounds = function (points) { + var i, + bounds = []; + for (i = 0; i < points.length; i++) { + bounds.push({ + 'lat': parseFloat(points[i].lat, 10), + 'lng': parseFloat(points[i].lng, 10) + }); + } + this.set('bounds', bounds); +}; + +GroundOverlay.prototype.getOpacity = function () { + return this.get('opacity'); +}; + +GroundOverlay.prototype.getBearing = function () { + return this.get('bearing'); +}; + +GroundOverlay.prototype.setOpacity = function (opacity) { + if (!opacity && opacity !== 0) { + console.log('opacity value must be int or double'); + return false; + } + this.set('opacity', opacity); +}; +GroundOverlay.prototype.setBearing = function (bearing) { + if (bearing > 360) { + bearing = bearing - Math.floor(bearing / 360) * 360; + } + this.set('bearing', bearing); +}; + +GroundOverlay.prototype.getZIndex = function () { + return this.get('zIndex'); +}; + +GroundOverlay.prototype.setZIndex = function (zIndex) { + this.set('zIndex', zIndex); +}; +GroundOverlay.prototype.setClickable = function (clickable) { + clickable = common.parseBoolean(clickable); + this.set('clickable', clickable); + return this; +}; +GroundOverlay.prototype.getClickable = function () { + return this.get('clickable'); +}; + +GroundOverlay.prototype.remove = function (callback) { + var self = this; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + self.trigger(self.__pgmId + '_remove'); + + var resolver = function(resolve, reject) { + self.exec.call(self, + function() { + self.destroy(); + resolve.call(self); + }, + reject.bind(self), + self.getPluginName(), 'remove', [self.getId()], { + remove: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + +}; + +module.exports = GroundOverlay; diff --git a/node_modules/cordova-plugin-googlemaps/www/HtmlInfoWindow.js b/node_modules/cordova-plugin-googlemaps/www/HtmlInfoWindow.js new file mode 100644 index 0000000..e73aa4d --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/HtmlInfoWindow.js @@ -0,0 +1,473 @@ + +var utils = require('cordova/utils'), + event = require('./event'), + BaseClass = require('./BaseClass'); + +/***************************************************************************** + * HTMLInfoWindow Class + *****************************************************************************/ +var HTMLInfoWindow = function () { + var self = this; + BaseClass.apply(self); + var callbackTable = {}; + var listenerMgr = { + one: function (target, eventName, callback) { + callbackTable[target.hashCode] = callbackTable[target.hashCode] || {}; + callbackTable[target.hashCode][eventName] = callbackTable[target.hashCode][eventName] || []; + callbackTable[target.hashCode][eventName].push(callback); + target.one.call(target, eventName, callback); + }, + on: function (target, eventName, callback) { + callbackTable[target.hashCode] = callbackTable[target.hashCode] || {}; + callbackTable[target.hashCode][eventName] = callbackTable[target.hashCode][eventName] || []; + callbackTable[target.hashCode][eventName].push(callback); + target.on.call(target, eventName, callback); + }, + bindTo: function (srcObj, srcField, dstObj, dstField, noNotify) { + var eventName = srcField + '_changed'; + dstField = dstField || srcField; + var callback = function (oldValue, newValue) { + dstObj.set(dstField, newValue, noNotify); + }; + callbackTable[dstObj.hashCode] = callbackTable[dstObj.hashCode] || {}; + callbackTable[dstObj.hashCode][eventName] = callbackTable[dstObj.hashCode][eventName] || []; + callbackTable[dstObj.hashCode][eventName].push(callback); + srcObj.on.call(srcObj, eventName, callback); + }, + off: function (target, eventName) { + if (!target || !eventName || + !(target.hashCode in callbackTable) || + !(eventName in callbackTable[target.hashCode])) { + return; + } + callbackTable[target.hashCode][eventName].forEach(function (listener) { + target.off.call(target, eventName, listener); + }); + delete callbackTable[target.hashCode][eventName]; + } + }; + + Object.defineProperty(self, '_hook', { + value: listenerMgr, + writable: false + }); + + var frame = document.createElement('div'); + frame.style.overflow = 'visible'; + frame.style.position = 'absolute'; + frame.style.display = 'inline-block'; + frame.classList.add('pgm-html-info-frame'); + self.set('frame', frame); + + var anchorDiv = document.createElement('div'); + anchorDiv.setAttribute('class', 'pgm-anchor'); + anchorDiv.style.overflow = 'visible'; + anchorDiv.style.position = 'absolute'; + anchorDiv.style['z-index'] = 0; + anchorDiv.style.width = '0 !important'; + anchorDiv.style.height = '0 !important'; + //anchorDiv.style.border = '1px solid green'; + //anchorDiv.style.backgroundColor = 'rgba(125, 125, 255, 0.5)'; + + anchorDiv.style.transition = 'transform 0s ease'; + anchorDiv.style['will-change'] = 'transform'; + + anchorDiv.style['-webkit-backface-visibility'] = 'hidden'; + anchorDiv.style['-webkit-perspective'] = 1000; + anchorDiv.style['-webkit-transition'] = '-webkit-transform 0s ease'; + + anchorDiv.appendChild(frame); + self.set('anchor', anchorDiv); + + var contentBox = document.createElement('div'); + contentBox.style.display = 'inline-block'; + contentBox.style.padding = '5px'; + contentBox.style.boxSizing = 'content-box'; + contentBox.classList.add('pgm-html-info-content-box'); + + var contentFrame = document.createElement('div'); + contentFrame.style.display = 'block'; + contentFrame.style.position = 'relative'; + contentFrame.style.backgroundColor = 'white'; + contentFrame.style.border = '1px solid rgb(204, 204, 204)'; + contentFrame.style.left = '0px'; + contentFrame.style.right = '0px'; + contentFrame.style.zIndex = '1'; // In order to set higher depth than the map div certainly + contentFrame.style.overflow = 'hidden'; + contentFrame.classList.add('pgm-html-info-content-frame'); + frame.appendChild(contentFrame); + contentFrame.appendChild(contentBox); + + var tailFrame = document.createElement('div'); + tailFrame.style.position = 'relative'; + tailFrame.style.top = '-1px'; + tailFrame.style.zIndex = 100; + tailFrame.classList.add('pgm-html-info-tail-frame'); + frame.appendChild(tailFrame); + + var tailLeft = document.createElement('div'); + /* + tailLeft.style.position = 'absolute'; + tailLeft.style.marginLeft = '-15px'; + tailLeft.style.left = '50%'; + tailLeft.style.top = '0px'; + tailLeft.style.height = '15px'; + tailLeft.style.width = '16px'; + tailLeft.style.overflow = 'hidden'; + tailLeft.style.borderWidth = '0px'; + */ + tailLeft.classList.add('pgm-html-info-tail-left'); + + tailLeft.style.position = 'absolute'; + tailLeft.style.left = '50%'; + tailLeft.style.height = '0px'; + tailLeft.style.width = '0px'; + tailLeft.style.marginLeft = '-15px'; + tailLeft.style.borderWidth = '15px 15px 0px'; + tailLeft.style.borderColor = 'rgb(204, 204, 204) transparent transparent'; + tailLeft.style.borderStyle = 'solid'; + tailFrame.appendChild(tailLeft); + + /* + var tailLeftCover = document.createElement('div'); + tailLeftCover.style.position = 'absolute'; + tailLeftCover.style.backgroundColor = 'white'; + tailLeftCover.style.transform = 'skewX(45deg)'; + tailLeftCover.style.transformOrigin = '0px 0px 0px'; + tailLeftCover.style.left = '0px'; + tailLeftCover.style.height = '15px'; + tailLeftCover.style.width = '15px'; + tailLeftCover.style.top = '0px'; + tailLeftCover.style.zIndex = 1; + tailLeftCover.style.borderLeft = '1px solid rgb(204, 204, 204)'; + tailLeft.classList.add('pgm-html-info-tail-left-cover'); + tailLeft.appendChild(tailLeftCover); + */ + + var tailRight = document.createElement('div'); + /* + tailRight.style.position = 'absolute'; + tailRight.style.left = '50%'; + tailRight.style.top = '0px'; + tailRight.style.height = '15px'; + tailRight.style.width = '16px'; + tailRight.style.overflow = 'hidden'; + tailRight.style.borderWidth = '0px'; + */ + tailRight.style.position = 'absolute'; + tailRight.style.left = '50%'; + tailRight.style.height = '0px'; + tailRight.style.width = '0px'; + tailRight.style.marginLeft = '-14px'; + tailRight.style.borderTopWidth = '14px'; + tailRight.style.borderLeftWidth = '14px'; + tailRight.style.borderRightWidth = '14px'; + tailRight.style.borderColor = 'rgb(255, 255, 255) transparent transparent'; + tailRight.style.borderStyle = 'solid'; + tailRight.classList.add('pgm-html-info-tail-right'); + tailFrame.appendChild(tailRight); + /* + var tailRightCover = document.createElement('div'); + tailRightCover.style.position = 'absolute'; + tailRightCover.style.backgroundColor = 'white'; + tailRightCover.style.transform = 'skewX(-45deg)'; + tailRightCover.style.transformOrigin = '0px 0px 0px'; + tailRightCover.style.left = '0px'; + tailRightCover.style.height = '15px'; + tailRightCover.style.width = '15px'; + tailRightCover.style.top = '0px'; + tailRightCover.style.zIndex = 2; + tailRightCover.style.borderRight = '1px solid rgb(204, 204, 204)'; + tailRightCover.classList.add('pgm-html-info-tail-right-cover'); + tailRight.appendChild(tailRightCover); + */ + var eraseBorder = document.createElement('div'); + eraseBorder.style.position = 'absolute'; + eraseBorder.style.zIndex = 3; + eraseBorder.style.backgroundColor = 'white'; + eraseBorder.style.width = '27px'; + eraseBorder.style.height = '2px'; + eraseBorder.style.top = '-1px'; + eraseBorder.style.left = '50%'; + eraseBorder.style.marginLeft = '-13px'; + eraseBorder.classList.add('pgm-html-info-tail-erase-border'); + tailFrame.appendChild(eraseBorder); + + var calculate = function (marker) { + + //var marker = self.get('marker'); + var map = marker.getMap(); + + var div = map.getDiv(); + + var frame = self.get('frame'); + var contentFrame = frame.firstChild; + var contentBox = contentFrame.firstChild; + contentBox.style.minHeight = '50px'; + contentBox.style.width = 'auto'; + contentBox.style.height = 'auto'; + contentBox.style.padding = '5px'; + + var content = self.get('content'); + if (typeof content === 'string') { + contentBox.style.whiteSpace = 'pre-wrap'; + contentBox.innerHTML = content; + } else { + if (!content) { + contentBox.innerText = ''; + } else if (content.nodeType === 1) { + contentBox.innerHTML = ''; + contentBox.appendChild(content); + } else { + contentBox.innerText = content; + } + } + + var cssOptions = self.get('cssOptions'); + if (cssOptions && typeof cssOptions === 'object') { + var keys = Object.keys(cssOptions); + keys.forEach(function (key) { + contentBox.style.setProperty(key, cssOptions[key]); + }); + } + // Insert the contents to this HTMLInfoWindow + if (!anchorDiv.parentNode) { + map._layers.info.appendChild(anchorDiv); + } + + // Adjust the HTMLInfoWindow size + var contentsWidth = contentBox.offsetWidth + 10; // padding 5px x 2 + var contentsHeight = contentBox.offsetHeight; + self.set('contentsHeight', contentsHeight); + contentFrame.style.width = contentsWidth + 'px'; + contentFrame.style.height = contentsHeight + 'px'; + frame.style.width = contentsWidth + 'px'; + frame.style.height = (contentsHeight + 15) + 'px'; + + if (contentBox.offsetWidth > div.offsetWidth * 0.9) { + contentBox.style.width = (div.offsetWidth * 0.9) + 'px'; + } + contentBox.style.width = '100%'; + contentBox.style.height = '100%'; + contentBox.style.padding = '5px 17px 17px 5px'; + self.set('contentsWidth', contentsWidth); + + var infoOffset = { + x: 31, + y: 31 + }; + var iconSize = { + width: 62, + height: 110 + }; + + // If there is no specification with `anchor` property, + // the values {x: 0.5, y: 1} are specified by native APIs. + // For the case, anchor values are {x: 0} in JS. + var anchor = { + x: 15, + y: 15 + }; + + var icon = marker.get('icon'); + + if (typeof icon === 'object') { + if (typeof icon.url === 'string' && icon.url.indexOf('data:image/') === 0) { + var img = document.createElement('img'); + img.src = icon.url; + iconSize.width = img.width; + iconSize.height = img.height; + } + if (typeof icon.size === 'object') { + iconSize.width = icon.size.width; + iconSize.height = icon.size.height; + } + + if (Array.isArray(icon.anchor)) { + anchor.x = icon.anchor[0]; + anchor.y = icon.anchor[1]; + } + } + + var infoWindowAnchor = marker.get('infoWindowAnchor'); + if (utils.isArray(infoWindowAnchor)) { + infoOffset.x = infoWindowAnchor[0]; + infoOffset.y = infoWindowAnchor[1]; + } + infoOffset.x = infoOffset.x / iconSize.width; + infoOffset.x = infoOffset.x > 1 ? 1 : infoOffset.x; + infoOffset.x = infoOffset.x < 0 ? 0 : infoOffset.x; + infoOffset.y = infoOffset.y / iconSize.height; + infoOffset.y = infoOffset.y > 1 ? 1 : infoOffset.y; + infoOffset.y = infoOffset.y < 0 ? 0 : infoOffset.y; + infoOffset.y *= iconSize.height; + infoOffset.x *= iconSize.width; + + anchor.x = anchor.x / iconSize.width; + anchor.x = anchor.x > 1 ? 1 : anchor.x; + anchor.x = anchor.x < 0 ? 0 : anchor.x; + anchor.y = anchor.y / iconSize.height; + anchor.y = anchor.y > 1 ? 1 : anchor.y; + anchor.y = anchor.y < 0 ? 0 : anchor.y; + anchor.y *= iconSize.height; + anchor.x *= iconSize.width; + + //console.log('contentsSize = ' + contentsWidth + ', ' + contentsHeight); + //console.log('iconSize = ' + iconSize.width + ', ' + iconSize.height); + //console.log('infoOffset = ' + infoOffset.x + ', ' + infoOffset.y); + + //var frameBorder = parseInt(common.getStyle(contentFrame, 'border-left-width').replace(/[^\d]/g, ''), 10); + //var offsetX = (contentsWidth + frameBorder + anchor.x ) * 0.5 + (iconSize.width / 2 - infoOffset.x); + //var offsetY = contentsHeight + anchor.y - (frameBorder * 2) - infoOffset.y + 15; + var offsetX = -(iconSize.width / 2) - (cordova.platformId === 'android' ? 1 : 0); + var offsetY = -iconSize.height - (cordova.platformId === 'android' ? 1 : 0); + anchorDiv.style.width = iconSize.width + 'px'; + anchorDiv.style.height = iconSize.height + 'px'; + + self.set('offsetX', offsetX); + self.set('offsetY', offsetY); + + frame.style.bottom = (iconSize.height - infoOffset.y) + 'px'; + frame.style.left = ((-contentsWidth) / 2 + infoOffset.x) + 'px'; + + //console.log('frameLeft = ' + frame.style.left ); + var point = map.get('infoPosition'); + anchorDiv.style.visibility = 'hidden'; + var x = point.x + self.get('offsetX'); + var y = point.y + self.get('offsetY'); + anchorDiv.style['-webkit-transform'] = 'translate3d(' + x + 'px, ' + y + 'px, 0px)'; + anchorDiv.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0px)'; + anchorDiv.style.visibility = 'visible'; + self.trigger('infoPosition_changed', '', point); + self.trigger(event.INFO_OPEN); + }; + + self._hook.on(self, 'infoPosition_changed', function (ignore, point) { + if (!point) return; + var x = point.x + self.get('offsetX'); + var y = point.y + self.get('offsetY'); + anchorDiv.style['-webkit-transform'] = 'translate3d(' + x + 'px, ' + y + 'px, 0px)'; + anchorDiv.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0px)'; + }); + + self._hook.on(self, 'infoWindowAnchor_changed', calculate); + + self.set('isInfoWindowVisible', false); + +}; + +utils.extend(HTMLInfoWindow, BaseClass); + +HTMLInfoWindow.prototype.isInfoWindowShown = function () { + return this.get('isInfoWindowVisible') === true; +}; + +HTMLInfoWindow.prototype.close = function () { + var self = this; + + var marker = self.get('marker'); + if (marker) { + self._hook.off(marker, 'isInfoWindowVisible_changed'); + } + if (!self.isInfoWindowShown() || !marker) { + return; + } + self.set('isInfoWindowVisible', false); + marker.set('isInfoWindowVisible', false); + marker.set('infoWindow', undefined); + this.set('marker', undefined); + + var map = marker.getMap(); + self._hook.off(marker.getMap(), 'map_clear'); + self._hook.off(marker, 'infoPosition_changed'); + self._hook.off(marker, 'icon_changed'); + //self._hook.off(self, 'infoWindowAnchor_changed'); + self._hook.off(marker, event.INFO_CLOSE); //This event listener is assigned in the open method. So detach it. + self.trigger(event.INFO_CLOSE); + map.set('active_marker', null); + + //var div = map.getDiv(); + var anchorDiv = self.get('anchor'); + if (anchorDiv && anchorDiv.parentNode) { + anchorDiv.parentNode.removeChild(anchorDiv); + + // Remove the contents from this HTMLInfoWindow + var contentFrame = anchorDiv.firstChild.firstChild; + var contentBox = contentFrame.firstChild; + contentBox.innerHTML = ''; + } +}; + +HTMLInfoWindow.prototype.setContent = function (content, cssOptions) { + var self = this; + var prevContent = self.get('content'); + self.set('content', content); + self.set('cssOptions', cssOptions); + var marker = self.get('marker'); + if (content !== prevContent && marker && marker.isInfoWindowShown()) { + var anchorDiv = self.get('anchor'); + if (anchorDiv) { + anchorDiv.style.width = '0 !important'; + anchorDiv.style.height = '0 !important'; + if (anchorDiv.parentNode) { + anchorDiv.parentNode.removeChild(anchorDiv); + + // Remove the contents from this HTMLInfoWindow + var contentFrame = anchorDiv.firstChild.firstChild; + var contentBox = contentFrame.firstChild; + contentBox.innerHTML = ''; + } + } + self.trigger('infoWindowAnchor_changed', marker); + } +}; + +HTMLInfoWindow.prototype.open = function (marker) { + if (!marker) { + return; + } + if (marker._objectInstance) { + // marker is an instance of the ionic-native wrapper plugin. + marker = marker._objectInstance; + } + + var self = this, + map = marker.getMap(); + + marker.set('infoWindow', self); + marker.set('isInfoWindowVisible', true); + self._hook.on(marker, 'icon_changed', function () { + self.trigger.call(self, 'infoWindowAnchor_changed', marker); + }); + self.set('isInfoWindowVisible', true); + self._hook.on(marker, 'isInfoWindowVisible_changed', function (prevValue, newValue) { + if (newValue === false) { + self.close.call(self); + } + }); + + map.fromLatLngToPoint(marker.getPosition(), function (point) { + map.set('infoPosition', { + x: point[0], + y: point[1] + }); + + self._hook.bindTo(map, 'infoPosition', self); + self._hook.bindTo(marker, 'infoWindowAnchor', self); + self._hook.bindTo(marker, 'icon', self); + self._hook.one(marker.getMap(), 'map_clear', self.close.bind(self)); + self._hook.one(marker, event.INFO_CLOSE, self.close.bind(self)); + self.set('marker', marker); + map.set('active_marker', marker); + self.trigger.call(self, 'infoWindowAnchor_changed', marker); + }); +}; + +HTMLInfoWindow.prototype.setBackgroundColor = function (backgroundColor) { + this.get('frame').children[0].style.backgroundColor = backgroundColor; + this.get('frame').children[1].children[0].style.borderColor = backgroundColor + ' rgba(0,0,0,0) rgba(0,0,0,0)'; + this.get('frame').children[1].children[1].style.borderColor = backgroundColor + ' rgba(0,0,0,0) rgba(0,0,0,0)'; + this.get('frame').children[1].children[2].style.backgroundColor = backgroundColor; +}; + +module.exports = HTMLInfoWindow; diff --git a/node_modules/cordova-plugin-googlemaps/www/InlineWorker.js b/node_modules/cordova-plugin-googlemaps/www/InlineWorker.js new file mode 100644 index 0000000..a969efe --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/InlineWorker.js @@ -0,0 +1,53 @@ +// InlineWorker +// https://github.com/mohayonao/inline-worker +// +// The MIT License (MIT) +// Copyright © 2018 Nao Yonamine +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var WORKER_ENABLED = !!window.Worker; +var global = window; + +function InlineWorker(func, self) { + var _this = this; + var functionBody; + + self = self || {}; + + if (WORKER_ENABLED) { + functionBody = func.toString().trim().match( + /^function\s*\w*\s*\([\w\s,]*\)\s*{([\w\W]*?)}$/ + )[1]; + + return new global.Worker(global.URL.createObjectURL( + new global.Blob([ functionBody ], { type: 'text/javascript' }) + )); + } + + function postMessage(data) { + setTimeout(function() { + _this.onmessage({ data: data }); + }, 0); + } + + this.self = self; + this.self.postMessage = postMessage; + + setTimeout(func.bind(self, self), 0); +} + +InlineWorker.prototype.postMessage = function postMessage(data) { + var _this = this; + + setTimeout(function() { + _this.self.onmessage({ data: data }); + }, 0); +}; + +module.exports = InlineWorker; diff --git a/node_modules/cordova-plugin-googlemaps/www/KmlLoader.js b/node_modules/cordova-plugin-googlemaps/www/KmlLoader.js new file mode 100644 index 0000000..5487833 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/KmlLoader.js @@ -0,0 +1,1115 @@ + + + +var utils = require('cordova/utils'), + event = require('./event'), + BaseClass = require('./BaseClass'), + BaseArrayClass = require('./BaseArrayClass'), + VisibleRegion = require('./VisibleRegion'); + +/***************************************************************************** + * KmlLoader Class + *****************************************************************************/ +var KmlLoader = function(map, exec, options) { + BaseClass.apply(this); + + var self = this; + //self.set("visible", KmlLoaderOptions.visible === undefined ? true : KmlLoaderOptions.visible); + //self.set("zIndex", KmlLoaderOptions.zIndex || 0); + Object.defineProperty(self, '_overlays', { + value: new BaseArrayClass(), + writable: false + }); + Object.defineProperty(self, '_isReady', { + value: true, + writable: false + }); + Object.defineProperty(self, 'type', { + value: 'KmlLoader', + writable: false + }); + Object.defineProperty(self, 'map', { + value: map, + writable: false + }); + Object.defineProperty(self, 'exec', { + value: exec, + writable: false + }); + Object.defineProperty(self, 'options', { + value: options, + writable: false + }); + Object.defineProperty(self, 'kmlUrl', { + value: options.url, + writable: false + }); + Object.defineProperty(self, 'camera', { + value: { + target: [] + }, + writable: false + }); +}; + +utils.extend(KmlLoader, BaseClass); + +KmlLoader.prototype.parseKmlFile = function(callback) { + var self = this; + + self.exec.call(self.map, function(kmlData) { + //var rawKmlData = JSON.parse(JSON.stringify(kmlData)); + //console.log(rawKmlData); + Object.defineProperty(self, 'kmlStyles', { + value: kmlData.styles, + writable: false + }); + Object.defineProperty(self, 'kmlSchemas', { + value: kmlData.schemas, + writable: false + }); + + var placeMarks = new BaseArrayClass(kmlData.root.children); + placeMarks.mapAsync(function(placeMark, cb) { + self.kmlTagProcess.call(self, { + child: placeMark, + attrHolder: {}, + styles: { + children: [] + } + }, cb); + }, function(placeMarkOverlays) { + placeMarkOverlays = placeMarkOverlays.filter(function(overlay) { + return !!overlay; + }); + var result = placeMarkOverlays.shift(); + //result.set('kmlData', rawKmlData); + callback.call(self, self.camera, result); + }); + }, self.map.errorHandler, self.map.__pgmId, 'loadPlugin', ['KmlOverlay', { + url: self.options.url + }], {sync: true}); +}; + +KmlLoader.prototype.kmlTagProcess = function(params, callback) { + var self = this; + + if (params.child.styleIDs) { + //--------------------------- + // Read styles if specified + //--------------------------- + var styleIDs = new BaseArrayClass(params.child.styleIDs); + styleIDs.mapAsync(function(styleId, cb) { + self.getStyleById.call(self, styleId, cb); + }, function(styleSets) { + + //----------------------------------- + // Merge styles with parent styles, + //----------------------------------- + var merged = {}; + styleSets.unshift(params.styles); + styleSets.forEach(function(styleSet) { + styleSet.children.forEach(function(style) { + merged[style.tagName] = merged[style.tagName] || {}; + style.children.forEach(function(styleEle) { + merged[style.tagName][styleEle.tagName] = styleEle; + }); + }); + }); + + params.styles = {}; + var keys = Object.keys(merged); + params.styles.children = keys.map(function(tagName) { + var properties = Object.keys(merged[tagName]); + var children = properties.map(function(propName) { + return merged[tagName][propName]; + }); + return { + tagName: tagName, + children: children + }; + }); + //------------------------ + // then process the tag. + //------------------------ + self.parseKmlTag.call(self, params, callback); + + }); + } else { + //------------------------------------------------- + // No styleID is specified, just process the tag + //------------------------------------------------- + self.parseKmlTag.call(self, params, callback); + } +}; + +KmlLoader.prototype.getObjectById = function(requestId, targetProp, callback) { + var self = this; + + if (!requestId) { + return callback.call(self, {children: []}); + } + var i, child, + results = {}; + if (requestId.indexOf('http://') === 0 || + requestId.indexOf('https://') === 0 || + requestId.indexOf('.kml') !== -1) { + + if (requestId.indexOf('://') === -1) { + requestId = self.kmlUrl.replace(/\/[^/]+$/, '/') + requestId; + } + //--------------------------- + // Load additional kml file + //--------------------------- + var requestUrl = requestId.replace(/#.*$/, ''); + var requestIdentify = requestId.replace(/^.*?#/, ''); + + if (requestUrl in self[targetProp]) { + self[targetProp][requestUrl] = self[targetProp][requestUrl] || {}; + results = self[targetProp][requestUrl][requestIdentify] || { + children: [] + }; + for (i = 0; i < results.children.length; i++) { + child = results.children[i]; + if (child.tagName === 'pair' && child.key === 'normal') { + return self.getObjectById.call(self, child.styleIDs[0], targetProp, callback); + } + } + callback.call(self, results); + return; + } + + var loader = new KmlLoader(self.map, self.exec, { + url: requestUrl + }); + loader.parseKmlFile(function(camera, anotherKmlData) { + var extendProps = [ + {src: 'styles', dst: 'kmlStyles'}, + {src: 'schemas', dst: 'kmlSchemas'} + ]; + extendProps.forEach(function(property) { + var properties = anotherKmlData.get('kmlData')[property.src]; + self[property.dst][requestUrl] = {}; + + var keys = Object.keys(properties); + keys.forEach(function(key) { + self[property.dst][requestUrl][key] = properties[key]; + }); + }); + + self[targetProp][requestUrl] = self[targetProp][requestUrl] || {}; + results = self[targetProp][requestUrl][requestIdentify] || { + children: [] + }; + for (i = 0; i < results.children.length; i++) { + child = results.children[i]; + if (child.tagName === 'pair' && child.key === 'normal') { + return self.getObjectById.call(self, child.styleIDs[0], targetProp, callback); + } + } + return callback.call(self, results); + }); + return; + } + + requestId = requestId.replace('#', ''); + if (requestId in self[targetProp] === false) { + callback.call(self, {children: []}); + return; + } + results = self[targetProp][requestId]; + + results.children.filter(function(style) { + if (style.tagName !== 'pair') { + return true; + } + for (var j = 0; j < style.children.length; j++) { + if (style.children[j].tagName === 'key' && + style.children[j].value === 'highlight') { + return false; + } + } + return true; + }); + + var containPairTag = false; + for (i = 0; i < results.children.length; i++) { + if (results.children[i].tagName === 'pair') { + containPairTag = true; + break; + } + } + if (!containPairTag) { + callback.call(self, results); + return; + } + + //--------------------------------------------------------- + // should contain 'tagName = "key", value="normal"' only + //--------------------------------------------------------- + self.getObjectById.call(self, results.children[0].styleIDs[0], targetProp, function(resultSets) { + if (resultSets.children) { + results = resultSets; + } else { + results.children = resultSets; + } + callback.call(self, results); + }); + +}; + +KmlLoader.prototype.getStyleById = function(requestId, callback) { + this.getObjectById.call(this, requestId, 'kmlStyles', callback); +}; + +KmlLoader.prototype.getSchemaById = function(requestId, callback) { + this.getObjectById.call(this, requestId, 'kmlSchemas', callback); +}; + +KmlLoader.prototype.parseKmlTag = function(params, callback) { + var self = this; + switch (params.child.tagName) { + case 'kml': + case 'folder': + case 'placemark': + case 'document': + case 'multigeometry': + self.parseContainerTag.call(self, { + placeMark: params.child, + styles: params.styles, + attrHolder: JSON.parse(JSON.stringify(params.attrHolder)) + }, callback); + break; + + case 'photooverlay': + case 'point': + self.parsePointTag.call(self, { + child: params.child, + placeMark: params.placeMark, + styles: params.styles, + attrHolder: params.attrHolder + }, callback); + break; + case 'polygon': + self.parsePolygonTag.call(self, { + child: params.child, + placeMark: params.placeMark, + styles: params.styles, + attrHolder: params.attrHolder + }, callback); + break; + case 'linestring': + self.parseLineStringTag.call(self, { + child: params.child, + placeMark: params.placeMark, + styles: params.styles, + attrHolder: params.attrHolder + }, callback); + break; + + case 'groundoverlay': + self.parseGroundOverlayTag.call(self, { + child: params.child, + placeMark: params.placeMark, + styles: params.styles, + attrHolder: params.attrHolder + }, callback); + break; + case 'networklink': + self.parseNetworkLinkTag.call(self, { + child: params.child, + placeMark: params.placeMark, + styles: params.styles, + attrHolder: params.attrHolder + }, callback); + break; + + case 'lookat': + self.parseLookAtTag.call(self, { + child: params.child, + }, callback); + break; + + case 'extendeddata': + self.parseExtendedDataTag.call(self, { + child: params.child, + placeMark: params.placeMark, + styles: params.styles, + attrHolder: params.attrHolder + }, callback); + break; + default: + params.attrHolder[params.child.tagName] = params.child; + callback(); + } +}; + +KmlLoader.prototype.parseExtendedDataTag = function(params, callback) { + var self = this; + if (params.child && params.child.children) { + params.attrHolder.extendeddata = {}; + params.child.children.forEach(function(child) { + switch(child.tagName) { + case 'data': + child.children.forEach(function(data) { + var dataName = child.name.toLowerCase(); + switch(data.tagName) { + case 'displayname': + params.attrHolder.extendeddata[dataName + '/displayname'] = data.value; + break; + case 'value': + params.attrHolder.extendeddata[dataName] = data.value; + break; + default: + break; + } + }); + break; + case 'schemadata': + self.getSchemaById(child.schemaUrl, function(schemas) { + var schemaUrl = schemas.name; + schemas.children.forEach(function(simplefield) { + if (simplefield.tagName !== 'simplefield') { + return; + } + if ('children' in simplefield) { + simplefield.children.forEach(function(valueTag) { + var schemaPath = schemaUrl + '/' + simplefield.name + '/' + valueTag.tagName; + schemaPath = schemaPath.toLowerCase(); + params.attrHolder.extendeddata[schemaPath] = valueTag.value; + }); + } else { + var schemaPath = schemaUrl + '/' + simplefield.name; + schemaPath = schemaPath.toLowerCase(); + params.attrHolder.extendeddata[schemaPath] = simplefield.value; + } + }); + child.children.forEach(function(simpledata) { + var schemaPath = schemaUrl + '/' + simpledata.name; + schemaPath = schemaPath.toLowerCase(); + params.attrHolder.extendeddata[schemaPath] = simpledata.value; + }); + }); + break; + + default: + + child.children.forEach(function(data) { + params.attrHolder.extendeddata[child.tagName] = data; + }); + break; + } + }); + } + callback(); +}; + +KmlLoader.prototype.parseContainerTag = function(params, callback) { + var self = this; + + var keys = Object.keys(params.placeMark); + keys = keys.filter(function(key) { + return key !== 'children'; + }); + + //-------------------------------------------------------- + // Generate overlays or load another files...etc + //-------------------------------------------------------- + var children = new BaseArrayClass(params.placeMark.children); + children.mapAsync(function(child, cb) { + + //------------------------- + // Copy parent information + //------------------------- + keys.forEach(function(key) { + if (key in child === false) { + child[key] = params.placeMark[key]; + } + }); + //------------------------- + // read a child element + //------------------------- + self.kmlTagProcess.call(self, { + child: child, + placeMark: params.placeMark, + styles: params.styles, + attrHolder: params.attrHolder + }, cb); + }, function(overlays) { + overlays = overlays.filter(function(overlay) { + return !!overlay; + }); + var attrNames = Object.keys(params.attrHolder); + if (overlays.length === 0) { + overlays.push(new BaseClass()); + } + + if (params.placeMark.tagName === 'placemark') { + // attrNames.forEach(function(name) { + // switch(name) { + // case "extendeddata": + // overlays[0].set(name, params.attrHolder[name]); + // break; + // case "snippet": + // overlays[0].set("_snippet", params.attrHolder[name].value); + // break; + // default: + // overlays[0].set(name, params.attrHolder[name].value); + // break; + // } + // }); + + callback.call(self, overlays[0]); + } else { + var container = new BaseArrayClass(overlays); + Object.defineProperty(container, 'tagName', { + value: params.placeMark.tagName, + writable: false + }); + attrNames.forEach(function(name) { + switch(name) { + case 'extendeddata': + container.set(name, params.attrHolder[name]); + break; + default: + container.set(name, params.attrHolder[name].value); + break; + } + }); + callback.call(self, container); + } + }); +}; + +KmlLoader.prototype.parsePointTag = function(params, callback) { + var self = this; + //console.log("parsePointTag", params); + + //-------------- + // add a marker + //-------------- + var markerOptions = { + visible: true + }; + params.styles.children.forEach(function(child) { + switch (child.tagName) { + + // // Don't use this code because this replace original 'description' field. + // case "balloonstyle": + // child.children.forEach(function(style) { + // switch (style.tagName) { + // case "description": + // markerOptions.description = { + // value: style.value + // }; + // break; + // } + // }); + // break; + + case 'iconstyle': + child.children.forEach(function(style) { + switch (style.tagName) { + case 'hotspot': + markerOptions.icon = markerOptions.icon || {}; + markerOptions.icon.hotspot = style; + break; + case 'heading': + markerOptions.icon = markerOptions.icon || {}; + markerOptions.icon.rotation = style; + break; + case 'icon': + markerOptions.icon = markerOptions.icon || {}; + markerOptions.icon.url = style.children[0].value; + break; + // case "color": + // markerOptions.icon = markerOptions.icon || {}; + // markerOptions.icon.color = kmlColorToRGBA(style.value); + // break; + } + }); + break; + default: + + } + }); + + if (params.child.children) { + var options = new BaseClass(); + params.child.children.forEach(function(child) { + options.set(child.tagName, child); + }); + params.child.children.forEach(function(child) { + switch (child.tagName) { + case 'point': + var coordinates = findTag(child.children, 'coordinates', 'coordinates'); + if (coordinates) { + markerOptions.position = coordinates[0]; + } + break; + case 'coordinates': + markerOptions.position = child.coordinates[0]; + break; + // case "description": + // if (markerOptions.description) { + // markerOptions.description = templateRenderer(markerOptions.description, options); + // } + // markerOptions.description = templateRenderer(markerOptions.description, options); + // break; + // case "snippet": + // if (markerOptions.snippet) { + // markerOptions.snippet = templateRenderer(markerOptions.snippet, options); + // } + // markerOptions.snippet = templateRenderer(markerOptions.snippet, options); + // break; + default: + + } + }); + } + markerOptions.position = markerOptions.position || { + lat: 0, + lng: 0 + }; + + self.camera.target.push(markerOptions.position); + + var ignoreProperties = ['coordinates', 'styleIDs', 'children']; + (Object.keys(params.attrHolder)).forEach(function(pName) { + if (ignoreProperties.indexOf(pName) === -1 && + pName in markerOptions === false) { + if (pName === 'visibility') { + markerOptions.visible = params.attrHolder[pName].value != 0; + } else { + markerOptions[pName] = params.attrHolder[pName]; + } + } + }); + + //console.log(markerOptions); + self.map.addMarker(markerOptions, callback); +}; + +function findTag(children, tagName, fieldName) { + for (var i = 0; i < children.length; i++) { + if (children[i].tagName === tagName) { + return children[i][fieldName]; + } + } +} +KmlLoader.prototype.parsePolygonTag = function(params, callback) { + var self = this; + + // console.log('polygonPlacemark', params); + //-------------- + // add a polygon + //-------------- + var polygonOptions = { + fill: true, + outline: true, + holes: [], + strokeWidth: 1, + clickable: true, + visible: true + }; + params.child.children.forEach(function(element) { + var coordinates; + switch (element.tagName) { + case'visibility': + polygonOptions.visible=element.value!=0; + break; + case 'outerboundaryis': + if (element.children.length === 1) { + switch(element.children[0].tagName) { + case 'linearring': + coordinates = element.children[0].children[0].coordinates; + break; + case 'coordinates': + coordinates = findTag(element.children, 'coordinates', 'coordinates'); + break; + } + coordinates.forEach(function(latLng) { + self.camera.target.push(latLng); + }); + polygonOptions.points = coordinates; + } + break; + case 'innerboundaryis': + switch(element.children[0].tagName) { + case 'linearring': + coordinates = element.children[0].children[0].coordinates; + break; + case 'coordinates': + coordinates = element.children[0].coordinates; + break; + } + polygonOptions.holes.push(coordinates); + break; + } + }); + + params.styles.children.forEach(function(style) { + switch (style.tagName) { + case 'polystyle': + style.children.forEach(function(node) { + switch(node.tagName) { + case 'color': + polygonOptions.fillColor = kmlColorToRGBA(node.value); + break; + case 'fill': + polygonOptions.fill = node.value === '1'; + break; + case 'outline': + polygonOptions.outline = node.value === '1'; + break; + } + }); + break; + + + case 'linestyle': + style.children.forEach(function(node) { + switch(node.tagName) { + case 'color': + polygonOptions.strokeColor = kmlColorToRGBA(node.value); + break; + case 'width': + polygonOptions.strokeWidth = parseFloat(node.value); + break; + } + }); + break; + } + }); + + if (polygonOptions.fill === false) { + polygonOptions.fillColor = [0, 0, 0, 0]; + } else { + polygonOptions.fillColor = polygonOptions.fillColor || [255, 255, 255, 255]; + } + if (polygonOptions.outline === false) { + delete polygonOptions.strokeColor; + polygonOptions.strokeWidth = 0; + } else { + polygonOptions.strokeColor = polygonOptions.strokeColor || [255, 255, 255, 255]; + } + + + var ignoreProperties = ['coordinates', 'styleIDs', 'children']; + (Object.keys(params.attrHolder)).forEach(function(pName) { + if (ignoreProperties.indexOf(pName) === -1 && + pName in polygonOptions === false) { + polygonOptions[pName] = params.attrHolder[pName]; + } + }); + + // console.log('polygonOptions', polygonOptions); + self.map.addPolygon(polygonOptions, callback); + +}; + +KmlLoader.prototype.parseLineStringTag = function(params, callback) { + //console.log(JSON.parse(JSON.stringify(params))); + var self = this; + //-------------- + // add a polyline + //-------------- + var polylineOptions = { + points: [], + clickable: true, + visible: true + }; + if (params.child.children) { + params.child.children.forEach(function(child) { + if (child.tagName === 'visibility') { + polylineOptions.visible=child.value!=0; + } + if (child.tagName === 'coordinates') { + child.coordinates.forEach(function(latLng) { + self.camera.target.push(latLng); + polylineOptions.points.push(latLng); + }); + } + }); + } + + params.styles.children.forEach(function(style) { + switch (style.tagName) { + case 'linestyle': + case 'polystyle': + style.children.forEach(function(node) { + switch(node.tagName) { + case 'color': + polylineOptions.color = kmlColorToRGBA(node.value); + break; + case 'width': + polylineOptions.width = parseFloat(node.value); + break; + } + }); + break; + default: + + } + }); + + var ignoreProperties = ['coordinates', 'styleIDs', 'children']; + (Object.keys(params.attrHolder)).forEach(function(pName) { + if (ignoreProperties.indexOf(pName) === -1 && + pName in polylineOptions === false) { + polylineOptions[pName] = params.attrHolder[pName]; + } + }); + + + //console.log('polylinePlacemark', polylineOptions); + + self.map.addPolyline(polylineOptions, callback); + +}; + +KmlLoader.prototype.parseGroundOverlayTag = function(params, callback) { + var self = this; + // console.log('parseGroundOverlayTag', params); + + //-------------- + // add a ground overlay + //-------------- + var groundoveralyOptions = { + url: null, + bounds: [], + clickable: true, + visible: true + }; + + params.child.children.forEach(function(child) { + switch (child.tagName) { + case'visibility': + groundoveralyOptions.visible=child.value!=0; + break; + case 'color': + groundoveralyOptions.opacity = ((kmlColorToRGBA(child.value)).pop() / 256); + break; + case 'icon': + child.children.forEach(function(iconAttrNode) { + switch (iconAttrNode.tagName) { + case 'href': + groundoveralyOptions.url = iconAttrNode.value; + if (groundoveralyOptions.url && groundoveralyOptions.url.indexOf('://') === -1) { + var requestUrl = self.kmlUrl.replace(/\?.*$/, ''); + requestUrl = requestUrl.replace(/#.*$/, ''); + requestUrl = requestUrl.replace(/[^/]*$/, ''); + groundoveralyOptions.url = requestUrl + groundoveralyOptions.url; + } + break; + } + }); + break; + case 'latlonbox': + var box = {}; + child.children.forEach(function(latlonboxAttrNode) { + box[latlonboxAttrNode.tagName] = parseFloat(latlonboxAttrNode.value); + }); + if (box.rotation) { + groundoveralyOptions.bearing = box.rotation; + } + var ne = {lat: box.north, lng: box.east}; + var sw = {lat: box.south, lng: box.west}; + groundoveralyOptions.bounds.push(ne); + groundoveralyOptions.bounds.push(sw); + self.camera.target.push(ne); + self.camera.target.push(sw); + break; + // case "gx:latlonquad": + // groundoveralyOptions.bounds = child.children[0].coordinates; + // Array.prototype.push.apply(self.camera.target, child.children[0].coordinates); + // break; + default: + } + }); + //delete params.child.children; + // console.log("groundoveralyOptions", groundoveralyOptions); + + var ignoreProperties = ['coordinates', 'styleIDs', 'children']; + (Object.keys(params.attrHolder)).forEach(function(pName) { + if (ignoreProperties.indexOf(pName) === -1 && + pName in groundoveralyOptions === false) { + groundoveralyOptions[pName] = params.attrHolder[pName]; + } + }); + + self.map.addGroundOverlay(groundoveralyOptions, callback); +}; + +KmlLoader.prototype.parseNetworkLinkTag = function(params, callback) { + var self = this; + var networkLinkOptions = {}; + //console.log('parseNetworkLinkTag', params); + + var attrNames = Object.keys(params.attrHolder); + attrNames.forEach(function(attrName) { + switch(attrName.toLowerCase()) { + case 'region': + networkLinkOptions.region = networkLinkOptions.region || {}; + params.attrHolder[attrName].children.forEach(function(gChild) { + switch(gChild.tagName) { + case 'latlonaltbox': + var box = {}; + gChild.children.forEach(function(latlonboxAttrNode) { + box[latlonboxAttrNode.tagName] = parseFloat(latlonboxAttrNode.value); + }); + networkLinkOptions.region.bounds = { + se: {lat: box.south, lng: box.east}, + sw: {lat: box.south, lng: box.west}, + ne: {lat: box.north, lng: box.east}, + nw: {lat: box.north, lng: box.west} + }; + break; + case 'lod': + networkLinkOptions.region.lod = networkLinkOptions.region.lod || {}; + networkLinkOptions.region.lod.minlodpixels = networkLinkOptions.region.lod.minlodpixels || -1; + networkLinkOptions.region.lod.maxlodpixels = networkLinkOptions.region.lod.maxlodpixels || -1; + gChild.children.forEach(function(lodEle) { + networkLinkOptions.region.lod[lodEle.tagName] = parseInt(lodEle.value); + }); + break; + } + }); + break; + + default: + networkLinkOptions[attrName] = params.attrHolder[attrName]; + } + }); + + params.child.children.forEach(function(child) { + switch(child.tagName) { + case 'visibility': + networkLinkOptions.visibility = child.value === '1'; + break; + case 'link': + networkLinkOptions.link = networkLinkOptions.link || {}; + child.children.forEach(function(gChild) { + networkLinkOptions.link[gChild.tagName] = gChild.value; + }); + break; + case 'region': + networkLinkOptions.region = networkLinkOptions.region || {}; + child.children.forEach(function(gChild) { + switch(gChild.tagName) { + case 'latlonaltbox': + var box = {}; + gChild.children.forEach(function(latlonboxAttrNode) { + box[latlonboxAttrNode.tagName] = parseFloat(latlonboxAttrNode.value); + }); + networkLinkOptions.region.bounds = { + se: {lat: box.south, lng: box.east}, + sw: {lat: box.south, lng: box.west}, + ne: {lat: box.north, lng: box.east}, + nw: {lat: box.north, lng: box.west} + }; + break; + case 'lod': + networkLinkOptions.region.lod = networkLinkOptions.region.lod || {}; + networkLinkOptions.region.lod.minlodpixels = networkLinkOptions.region.lod.minlodpixels || -1; + networkLinkOptions.region.lod.maxlodpixels = networkLinkOptions.region.lod.maxlodpixels || -1; + gChild.children.forEach(function(lodEle) { + networkLinkOptions.region.lod[lodEle.tagName] = parseInt(lodEle.value); + }); + break; + } + }); + + } + }); + + //console.log(networkLinkOptions); + + if (!networkLinkOptions.link) { + // tag must contain tag. + // If not contained, simply ignore the tag. + return callback.call(self); + } + + + if (networkLinkOptions.link.href.indexOf('://') === -1 && networkLinkOptions.link.href.substr(0, 1) !== '/') { + var a = document.createElement('a'); + a.href = self.kmlUrl; + networkLinkOptions.link.href = a.protocol + '//' + a.host + ':' + a.port + a.pathname.replace(/\/[^/]+$/, '') + '/' + networkLinkOptions.link.href; + a = null; + } + + var networkOverlay = new BaseClass(); + networkOverlay.set('_loaded', false); + networkOverlay.set('_visible', false); + networkOverlay.on('_visible_changed', function(oldValue, newValue) { + var overlay = networkOverlay.get('overlay'); + if (newValue === true) { + if (overlay) { + overlay.setVisible(true); + } else { + self.map.addKmlOverlay({ + url: networkLinkOptions.link.href, + clickable: self.options.clickable, + suppressInfoWindows: self.options.suppressInfoWindows + }, function(overlay) { + networkOverlay.set('overlay', overlay); + }); + } + } else { + if (overlay) { + overlay.setVisible(false); + } + } + }); + self._overlays.push(networkOverlay); + + if (networkLinkOptions.region && networkLinkOptions.region.bounds) { + self.camera.target.push(networkLinkOptions.region.bounds.se); + self.camera.target.push(networkLinkOptions.region.bounds.sw); + self.camera.target.push(networkLinkOptions.region.bounds.ne); + self.camera.target.push(networkLinkOptions.region.bounds.nw); + } + // self.map.addPolygon({ + // 'points': [ + // networkLinkOptions.region.bounds.se, + // networkLinkOptions.region.bounds.sw, + // networkLinkOptions.region.bounds.nw, + // networkLinkOptions.region.bounds.ne + // ], + // 'strokeColor' : '#FFFFFF77', + // 'strokeWidth': 1, + // 'fillColor' : '#00000000' + // }, function(groundoverlay) { + + if (networkLinkOptions.region && networkLinkOptions.link.viewrefreshmode === 'onRegion') { + self.map.on(event.CAMERA_MOVE_END, function() { + var vRegion = self.map.getVisibleRegion(); + var nRegion = new VisibleRegion(networkLinkOptions.region.bounds.sw, networkLinkOptions.region.bounds.ne); + + if (vRegion.contains(networkLinkOptions.region.bounds.sw) || + vRegion.contains(networkLinkOptions.region.bounds.se) || + vRegion.contains(networkLinkOptions.region.bounds.nw) || + vRegion.contains(networkLinkOptions.region.bounds.ne) || + nRegion.contains(vRegion.farLeft) || + nRegion.contains(vRegion.farRight) || + nRegion.contains(vRegion.nearLeft) || + nRegion.contains(vRegion.nearRight)) { + + (new BaseArrayClass([ + networkLinkOptions.region.bounds.sw, + networkLinkOptions.region.bounds.ne + ]).mapAsync(function(latLng, next) { + self.map.fromLatLngToPoint(latLng, next); + }, function(points) { + var width = Math.abs(points[0][0] - points[1][0]); + var height = Math.abs(points[0][1] - points[1][1]); + + var maxCondition = (networkLinkOptions.region.lod.maxlodpixels === -1 || + width <= networkLinkOptions.region.lod.maxlodpixels && + height <= networkLinkOptions.region.lod.maxlodpixels); + var minCondition = (networkLinkOptions.region.lod.minlodpixels === -1 || + width >= networkLinkOptions.region.lod.minlodpixels && + height >= networkLinkOptions.region.lod.minlodpixels); + + if (maxCondition && minCondition) { + // groundoverlay.setVisible(true); + networkOverlay.set('_visible', true); + } else { + // groundoverlay.setVisible(false); + networkOverlay.set('_visible', false); + } + })); + } else { + // groundoverlay.setVisible(false); + networkOverlay.set('_visible', false); + } + }); + } else { + //------------------------------- + // Simply load another kml file + //------------------------------- + // groundoverlay.setVisible(true); + networkOverlay.set('_visible', networkLinkOptions.visibility); + } + + callback.call(networkOverlay); + //}); + +}; + +KmlLoader.prototype.parseLookAtTag = function(params, callback) { + var self = this; + + if ('latitude' in params.child && 'longitude' in params.child) { + self.camera.target = { + lat: parseFloat(params.child.latitude), + lng: parseFloat(params.child.longitude) + }; + } + if ('heading' in params.child) { + self.camera.bearing = parseInt(params.child.heading); + } + if ('tilt' in params.child) { + self.camera.tilt = parseInt(params.child.tilt); + } + + callback.call(self); +}; + + +//------------------------------- +// KML color (AABBGGRR) to RGBA +//------------------------------- +function kmlColorToRGBA(colorStr) { + var rgba = []; + colorStr = colorStr + ''; // convert to String mandatory + colorStr = colorStr.replace('#', ''); + for (var i = 6; i >= 0; i -= 2) { + rgba.push(parseInt(colorStr.substring(i, i + 2), 16)); + } + return rgba; +} +//------------------------------- +// KML color (AABBGGRR) to rgba(RR, GG, BB, AA) +//------------------------------- +// function kmlColorToCSS(colorStr) { +// var rgba = []; +// colorStr = colorStr.replace('#', ''); +// for (var i = 6; i >= 0; i -= 2) { +// rgba.push(parseInt(colorStr.substring(i, i + 2), 16)); +// } +// return 'rgba(' + rgba.join(',') + ')'; +// } + +//------------------------------- +// Template engine +//------------------------------- +// function templateRenderer(html, marker) { +// if (!html) { +// return html; +// } +// var extendedData = marker.get('extendeddata'); +// +// return html.replace(/\$[{[](.+?)[}]]/gi, function(match, name) { +// var textProp = marker.get(name); +// var text = ''; +// if (textProp) { +// text = textProp.value; +// if (extendedData) { +// text = text.replace(/\$[{[](.+?)[}]]/gi, function(match1, name1) { +// var extProp = extendedData[name1.toLowerCase()]; +// var extValue = '${' + name1 + '}'; +// if (extProp) { +// extValue = extProp.value; +// } +// return extValue; +// }); +// } +// } +// return text; +// }); +// } + + +module.exports = KmlLoader; diff --git a/node_modules/cordova-plugin-googlemaps/www/KmlOverlay.js b/node_modules/cordova-plugin-googlemaps/www/KmlOverlay.js new file mode 100644 index 0000000..b6c9c29 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/KmlOverlay.js @@ -0,0 +1,368 @@ + +var utils = require('cordova/utils'), + common = require('./Common'), + BaseClass = require('./BaseClass'), + event = require('./event'), + BaseArrayClass = require('./BaseArrayClass'), + HtmlInfoWindow = require('./HtmlInfoWindow'); + +var XElement = null; +window.addEventListener('WebComponentsReady', function () { + var XElementPrototype = Object.create(HTMLElement.prototype); + XElement = document.registerElement('pgm-sandbox', { + prototype: XElementPrototype + }); +}); + +/***************************************************************************** + * KmlOverlay Class + *****************************************************************************/ +var KmlOverlay = function (map, kmlId, camera, kmlData, kmlOverlayOptions) { + BaseClass.apply(this); + + var self = this; + //self.set('visible', kmlOverlayOptions.visible === undefined ? true : kmlOverlayOptions.visible); + //self.set('zIndex', kmlOverlayOptions.zIndex || 0); + Object.defineProperty(self, '_isReady', { + value: true, + writable: false + }); + Object.defineProperty(self, 'type', { + value: 'KmlOverlay', + writable: false + }); + Object.defineProperty(self, '__pgmId', { + value: kmlId, + writable: false + }); + Object.defineProperty(self, 'map', { + value: map, + writable: false + }); + Object.defineProperty(self, 'camera', { + value: camera, + writable: false + }); + Object.defineProperty(self, 'kmlData', { + value: kmlData, + writable: false + }); + + + function templateRenderer(html, marker) { + var extendedData = marker.get('extendeddata'); + + return html.replace(/\$[{\\[](.+?)[}\]]/gi, function(match, name) { + var text = ''; + if (marker.get(name)) { + text = marker.get(name).value || ''; + } + if (extendedData && text) { + text = text.replace(/\$[{\\[](.+?)[}\]]/gi, function(match1, name1) { + return extendedData[name1.toLowerCase()] || ''; + }); + } + return text; + }); + } + + function parseBalloonStyle(balloonStyle) { + var css = {}; + var hasBgColor = false; + var keys = Object.keys(balloonStyle); + keys.forEach(function (key) { + switch (key.toLowerCase()) { + case 'bgcolor': + hasBgColor = true; + ballon.setBackgroundColor(common.kmlColorToCSS(balloonStyle[key])); + break; + case 'textcolor': + css.color = common.kmlColorToCSS(balloonStyle[key]); + break; + } + }); + if (!hasBgColor) { + ballon.setBackgroundColor('white'); + } + return css; + } + + self.set('clickable', kmlOverlayOptions.clickable); + + var ballon = new HtmlInfoWindow(); + var onOverlayClick = function (position, overlay) { + if (!self.get('clickable')) { + return; + } + self.trigger(event.KML_CLICK, overlay, position); + + if (kmlOverlayOptions.suppressInfoWindows) { + return; + } + + var description = overlay.get('description'); + if (!description && overlay.get('extendeddata')) { + var extendeddata = overlay.get('extendeddata'); + var keys = Object.keys(extendeddata); + var table = []; + keys.forEach(function (key) { + if (extendeddata[key] !== '' && extendeddata[key] !== null && extendeddata[key] !== undefined) { + table.push('' + key + '' + extendeddata[key] + ''); + } + }); + if (table.length > 0) { + table.unshift(''); + table.push('
'); + description = { + value: table.join('') + }; + overlay.set('description', description); + } + } + + var html = []; + var result; + var descriptionTxt = ''; + if (description && description.value) { + descriptionTxt = description.value; + } + if (description && (descriptionTxt.indexOf('') > -1 || descriptionTxt.indexOf('script') > -1)) { + var text = templateRenderer(descriptionTxt, overlay); + // create a sandbox + // if (text.indexOf(''; + // } + result = document.createElement('div'); + if (overlay.get('name') && overlay.get('name').value) { + var name = document.createElement('div'); + name.style.fontWeight = 500; + name.style.fontSize = 'medium'; + name.style.marginBottom = 0; + name.style.whiteSpace = 'pre'; + name.innerText = overlay.get('name').value || ''; + result.appendChild(name); + } + if (overlay.get('snippet') && overlay.get('snippet').value.length > 0) { + var snippet = document.createElement('div'); + snippet.style.fontWeight = 300; + snippet.style.fontSize = 'small'; + snippet.style.whiteSpace = 'normal'; + snippet.style.whiteSpace = 'pre'; + snippet.style.fontFamily = 'Roboto,Arial,sans-serif'; + snippet.innerText = overlay.get('snippet').value || ''; + result.appendChild(snippet); + } + + if (text && text.length > 0) { + if (XElement) { + var xElement = new XElement(); + xElement.innerHTML = text; + result.appendChild(xElement); + } else { + + var iframe = document.createElement('iframe'); + iframe.sandbox = 'allow-scripts allow-same-origin'; + iframe.frameBorder = 'no'; + iframe.scrolling = 'yes'; + iframe.style.overflow = 'hidden'; + iframe.addEventListener('load', function () { + iframe.contentWindow.document.open(); + iframe.contentWindow.document.write(text); + iframe.contentWindow.document.close(); + }, { + once: true + }); + result.appendChild(iframe); + + } + } + + } else { + if (overlay.get('description')) { + html.push('
${description}
'); + } else { + if (overlay.get('name')) { + html.push('
${name}
'); + } + if (overlay.get('_snippet')) { + html.push('
${_snippet}
'); + } + } + var prevMatchedCnt = 0; + result = html.join(''); + var matches = result.match(/\$[{\\[].+?[}\]]/gi); + while(matches && matches.length !== prevMatchedCnt) { + prevMatchedCnt = matches.length; + result = templateRenderer(result, overlay); + matches = result.match(/\$[{\\[].+?[}\]]/gi); + } + } + var styles = null; + if (overlay.get('balloonstyle')) { + styles = parseBalloonStyle(overlay.get('balloonstyle')); + } + styles = styles || {}; + styles.overflow = 'scroll'; + styles['max-width'] = (map.getDiv().offsetWidth * 0.8) + 'px'; + styles['max-height'] = (map.getDiv().offsetHeight * 0.6) + 'px'; + + ballon.setContent(result, styles); + var marker = map.get('invisible_dot'); + if (overlay.type === 'Marker') { + marker.setVisible(false); + ballon.open(overlay); + } else { + marker.setPosition(position); + marker.setVisible(true); + marker.off(event.MARKER_CLICK); + map.animateCamera({ + target: position, + duration: 300 + }, function () { + marker.setAnimation(window.plugin.google.maps.Animation.DROP, function () { + marker.on(event.MARKER_CLICK, function () { + ballon.open(marker); + }); + marker.trigger(event.MARKER_CLICK); + }); + }); + + } + }; + + var eventNames = { + 'Marker': event.MARKER_CLICK, + 'Polyline': event.POLYLINE_CLICK, + 'Polygon': event.POLYGON_CLICK, + 'GroundOverlay': event.GROUND_OVERLAY_CLICK + }; + var seekOverlays = function (overlay) { + if (overlay instanceof BaseArrayClass) { + overlay.forEach(seekOverlays); + } else if (Array.isArray(overlay)) { + (new BaseArrayClass(overlay)).forEach(seekOverlays); + } else if (overlay instanceof BaseClass && overlay.type in eventNames) { + overlay.on(eventNames[overlay.type], onOverlayClick); + } + }; + + kmlData.forEach(seekOverlays); + + /* + var ignores = ['map', '__pgmId', 'hashCode', 'type']; + for (var key in kmlOverlayOptions) { + if (ignores.indexOf(key) === -1) { + self.set(key, kmlOverlayOptions[key]); + } + } + */ +}; + +utils.extend(KmlOverlay, BaseClass); + +KmlOverlay.prototype.getPluginName = function () { + return this.map.getId() + '-kmloverlay'; +}; + +KmlOverlay.prototype.getHashCode = function () { + return this.hashCode; +}; + +KmlOverlay.prototype.getDefaultViewport = function () { + return this.camera; +}; +KmlOverlay.prototype.getKmlData = function () { + return this.kmlData; +}; +KmlOverlay.prototype.getMap = function () { + return this.map; +}; +KmlOverlay.prototype.getId = function () { + return this.__pgmId; +}; + +KmlOverlay.prototype.setClickable = function (clickable) { + clickable = common.parseBoolean(clickable); + this.set('clickable', clickable); + return this; +}; + +KmlOverlay.prototype.getClickable = function () { + return this.get('clickable'); +}; + +KmlOverlay.prototype.getVisible = function () { + return this.get('visible'); +}; + +KmlOverlay.prototype.setVisible = function (visible) { + var self = this; + if (self._isRemoved) { + return; + } + + visible = common.parseBoolean(visible); + this.set('visible', visible); + + var applyChildren = function (children) { + children.forEach(function (child) { + if ('setVisible' in child && + typeof child.setVisible === 'function') { + child.setVisible(visible); + return; + } + if (child instanceof BaseArrayClass) { + applyChildren(child); + return; + } + }); + }; + + applyChildren(self.kmlData); +}; + +KmlOverlay.prototype.remove = function (callback) { + var self = this; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + + var removeChildren = function (children, cb) { + children.forEach(function (child, next) { + if ('remove' in child && + typeof child.remove === 'function') { + child.remove(next); + return; + } + if (child instanceof BaseArrayClass) { + removeChildren(child, next); + return; + } + next(); + }, cb); + }; + + var resolver = function (resolve) { + removeChildren(self.kmlData, function () { + self.destroy(); + resolve.call(self); + }); + }; + + if (typeof callback === 'function') { + resolver.call(self, callback, null); + return; + } else { + return Promise.resolve(); + } +}; + +module.exports = KmlOverlay; diff --git a/node_modules/cordova-plugin-googlemaps/www/LatLng.js b/node_modules/cordova-plugin-googlemaps/www/LatLng.js new file mode 100644 index 0000000..5356ec6 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/LatLng.js @@ -0,0 +1,51 @@ +/******************************************************************************* + * @name LatLng + * @class This class represents new camera position + * @param {Number} latitude + * @param {Number} longitude + ******************************************************************************/ +function LatLng(latitude, longitude) { + var self = this; + /** + * @property {Number} latitude + */ + self.lat = parseFloat(latitude || 0, 10); + + /** + * @property {Number} longitude + */ + self.lng = parseFloat(longitude || 0, 10); +} + +LatLng.prototype = { + /** + * Comparison function. + * @method + * @return {Boolean} + */ + equals: function(other) { + other = other || {}; + return other.lat === this.lat && + other.lng === this.lng; + }, + + /** + * @method + * @return {String} latitude,lontitude + */ + toString: function() { + return '{"lat": ' + this.lat + ', "lng": ' + this.lng + '}'; + }, + + /** + * @method + * @param {Number} + * @return {String} latitude,lontitude + */ + toUrlValue: function(precision) { + precision = precision || 6; + return '{"lat": ' + this.lat.toFixed(precision) + ', "lng": ' + this.lng.toFixed(precision) + '}'; + } +}; + +module.exports = LatLng; diff --git a/node_modules/cordova-plugin-googlemaps/www/LatLngBounds.js b/node_modules/cordova-plugin-googlemaps/www/LatLngBounds.js new file mode 100644 index 0000000..70f32f9 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/LatLngBounds.js @@ -0,0 +1,130 @@ +var LatLng = require('./LatLng'); + +/***************************************************************************** + * LatLngBounds Class + *****************************************************************************/ +var LatLngBounds = function () { + Object.defineProperty(this, 'type', { + value: 'LatLngBounds', + writable: false + }); + + var args = []; + if (arguments.length === 1 && + typeof arguments[0] === 'object' && + 'push' in arguments[0]) { + args = arguments[0]; + } else { + args = Array.prototype.slice.call(arguments, 0); + } + + for (var i = 0; i < args.length; i++) { + if (args[i] && 'lat' in args[i] && 'lng' in args[i]) { + this.extend(args[i]); + } + } +}; + +LatLngBounds.prototype.northeast = null; +LatLngBounds.prototype.southwest = null; + +LatLngBounds.prototype.toString = function () { + return '{"southwest":' + this.southwest.toString() + ', "northeast":' + this.northeast.toString() + '}'; +}; +LatLngBounds.prototype.toUrlValue = function (precision) { + precision = precision || 6; + return '[' + this.southwest.toUrlValue(precision) + ',' + this.northeast.toUrlValue(precision) + ']'; +}; + +LatLngBounds.prototype.extend = function (latLng) { + if (latLng && 'lat' in latLng && 'lng' in latLng) { + if (!this.southwest && !this.northeast) { + this.southwest = latLng; + this.northeast = latLng; + } else { + var south = Math.min(latLng.lat, this.southwest.lat); + var north = Math.max(latLng.lat, this.northeast.lat); + + var west = this.southwest.lng, + east = this.northeast.lng; + + if (west > 0 && east < 0) { + if (latLng.lng > 0) { + west = Math.min(latLng.lng, west); + } else { + east = Math.max(latLng.lng, east); + } + } else { + + west = Math.min(latLng.lng, this.southwest.lng); + east = Math.max(latLng.lng, this.northeast.lng); + } + + delete this.southwest; + delete this.northeast; + this.southwest = new LatLng(south, west); + this.northeast = new LatLng(north, east); + } + } +}; + +LatLngBounds.prototype.getCenter = function () { + var centerLat = (this.southwest.lat + this.northeast.lat) / 2; + + var swLng = this.southwest.lng; + var neLng = this.northeast.lng; + var sumLng = swLng + neLng; + var centerLng = sumLng / 2; + + if ((swLng > 0 && neLng < 0 && sumLng < 180)) { + centerLng += sumLng > 0 ? -180 : 180; + } + return new LatLng(centerLat, centerLng); +}; +LatLngBounds.prototype.contains = function (latLng) { + if (!latLng || !('lat' in latLng) || !('lng' in latLng)) { + return false; + } + var y = latLng.lat, + x = latLng.lng; + + var y90 = y + 90; + var south = this.southwest.lat, + north = this.northeast.lat, + west = this.southwest.lng, + east = this.northeast.lng; + var south90 = south + 90, + north90 = north + 90; + + var containX = false, + containY = false; + + if (west <= 0 && east <= 0 && west <= east) { + if (x > 0) { + containX = false; + } else { + containX = (west <= x && x <= east); + } + } else if (east <= 0 && west > 0 && east <= west) { + if (x > 0) { + containX = (x >= west && x <= 180); + } else { + containX = (-180 <= x && x <= west); + } + } else if (west <= 0 && east > 0 && west <= east) { + if (x < 0) { + containX = (west <= x && x <= 0); + } else { + containX = (x >= 0 && x <= east); + } + } else { + containX = (west <= x && x <= east); + } + + containY = (south90 <= y90 && y90 <= north90) || //#a + (south >= 0 && north <= 0 && ((south <= y && y <= 90) || (y >= -90 && y <= north))); // #d + + return containX && containY; +}; + +module.exports = LatLngBounds; diff --git a/node_modules/cordova-plugin-googlemaps/www/Location.js b/node_modules/cordova-plugin-googlemaps/www/Location.js new file mode 100644 index 0000000..49c47d0 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Location.js @@ -0,0 +1,18 @@ +/***************************************************************************** + * Location Class + *****************************************************************************/ +var LatLng = require('./LatLng'); +var Location = function(params) { + var self = this; + self.latLng = params.latLng || new LatLng(params.lat || 0, params.lng || 0); + self.elapsedRealtimeNanos = params.elapsedRealtimeNanos; + self.time = params.time; + self.accuracy = params.accuracy || null; + self.bearing = params.bearing || null; + self.altitude = params.altitude || null; + self.speed = params.speed || null; + self.provider = params.provider; + self.hashCode = params.hashCode; +}; + +module.exports = Location; diff --git a/node_modules/cordova-plugin-googlemaps/www/LocationService.js b/node_modules/cordova-plugin-googlemaps/www/LocationService.js new file mode 100644 index 0000000..827f1d5 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/LocationService.js @@ -0,0 +1,114 @@ +/***************************************************************************** + * LocationService class + *****************************************************************************/ +var LatLng = require('./LatLng'); + +var LocationService = function(exec) { + function _errorHandler(err) { + console.error(err); + } + return { + hasPermission: function(callback, errorCallback) { + var self = this; + + var resolver = function(resolve, reject) { + exec.call({ + _isReady: true + }, + function(hasPermission) { + resolve.call(self, hasPermission === 1); + }, + reject.bind(self), 'PluginLocationService', 'hasPermission', [], {sync: true}); + }; + + var errorHandler = function(result) { + if (typeof errorCallback === 'function') { + errorCallback.call(self, result); + } else { + (self.errorHandler || _errorHandler).call(self, result); + } + }; + if (typeof callback === 'function') { + resolver(callback, errorHandler); + return self; + } else { + return new Promise(resolver); + } + }, + getMyLocation: function(params, success_callback, errorCallback) { + var self = this; + var args = [params || {}, success_callback || null, errorCallback]; + if (typeof args[0] === 'function') { + args.unshift({}); + } + params = args[0]; + success_callback = args[1]; + errorCallback = args[2]; + + + var resolver = function(resolve, reject) { + exec.call({ + _isReady: true + }, + function(location) { + location.latLng = new LatLng(location.latLng.lat, location.latLng.lng); + resolve.call(self, location); + }, + reject.bind(self), 'PluginLocationService', 'getMyLocation', [params], {sync: true}); + }; + + params.enableHighAccuracy = params.enableHighAccuracy === true; + var errorHandler = function(result) { + if (typeof errorCallback === 'function') { + errorCallback.call(self, result); + } else { + (self.errorHandler || _errorHandler).call(self, result); + } + }; + + if (typeof success_callback === 'function') { + resolver(success_callback, errorHandler); + return self; + } else { + return new Promise(resolver); + } + } + }; +}; + +/** + // TODO: +LocationService.prototype.followMyPosition = function(params, success_callback, errorCallback) { + var self = this; + var args = [params || {}, success_callback || null, errorCallback]; + if (typeof args[0] === 'function') { + args.unshift({}); + } + self.on('currentPosition_changed', success_callback); + var successHandler = function(location) { + location.latLng = new LatLng(location.latLng.lat, location.latLng.lng); + if (typeof success_callback === 'function') { + success_callback.call(self, location); + } + self.set('currentPosition', location); + }; + var errorHandler = function(result) { + if (typeof errorCallback === 'function') { + errorCallback.call(self, result); + } + }; + exec.call({ + _isReady: true + }, successHandler, errorHandler, 'CordovaGoogleMaps', 'followMyPosition', [params], {sync: true}); +}; + +LocationService.prototype.clearFollowing = function() { + var self = this; + self.off('currentPosition_changed'); + exec.call({ + _isReady: true + }, successHandler, errorHandler, 'CordovaGoogleMaps', 'clearFollowing', [params], {sync: true}); +}; +**/ + +module.exports = LocationService; diff --git a/node_modules/cordova-plugin-googlemaps/www/Map.js b/node_modules/cordova-plugin-googlemaps/www/Map.js new file mode 100644 index 0000000..80373fc --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Map.js @@ -0,0 +1,1589 @@ + + +var utils = require('cordova/utils'), + cordova_exec = require('cordova/exec'), + common = require('./Common'), + Overlay = require('./Overlay'), + BaseClass = require('./BaseClass'), + BaseArrayClass = require('./BaseArrayClass'), + LatLng = require('./LatLng'), + MapTypeId = require('./MapTypeId'), + event = require('./event'), + VisibleRegion = require('./VisibleRegion'), + Marker = require('./Marker'), + Circle = require('./Circle'), + Polyline = require('./Polyline'), + Polygon = require('./Polygon'), + TileOverlay = require('./TileOverlay'), + GroundOverlay = require('./GroundOverlay'), + KmlOverlay = require('./KmlOverlay'), + KmlLoader = require('./KmlLoader'), + MarkerCluster = require('./MarkerCluster'); + +/** + * Google Maps model. + */ +var exec; +var Map = function(__pgmId, _exec) { + var self = this; + exec = _exec; + Overlay.call(self, self, {}, 'Map', _exec, { + __pgmId: __pgmId + }); + delete self.map; + + + self.set('myLocation', false); + self.set('myLocationButton', false); + + self.MARKERS = {}; + self.OVERLAYS = {}; + + var infoWindowLayer = document.createElement('div'); + infoWindowLayer.style.position = 'absolute'; + infoWindowLayer.style.left = 0; + infoWindowLayer.style.top = 0; + infoWindowLayer.style.width = 0; + infoWindowLayer.style.height = 0; + infoWindowLayer.style.overflow = 'visible'; + infoWindowLayer.style['z-index'] = 1; + + Object.defineProperty(self, '_layers', { + value: { + info: infoWindowLayer + }, + enumerable: false, + writable: false + }); + + self.on(event.MAP_CLICK, function() { + self.set('active_marker', undefined); + }); + + self.on('active_marker_changed', function(prevMarker, newMarker) { + var newMarkerId = newMarker ? newMarker.getId() : null; + if (prevMarker) { + prevMarker.hideInfoWindow.call(prevMarker); + } + self.exec.call(self, null, null, self.__pgmId, 'setActiveMarkerId', [newMarkerId]); + }); +}; + +utils.extend(Map, Overlay); + +/** + * @desc Recalculate the position of HTML elements + */ +Map.prototype.refreshLayout = function() { + // Webkit redraw mandatory + // http://stackoverflow.com/a/3485654/697856 + document.body.style.display = 'inline-block'; + document.body.offsetHeight; + document.body.style.display = ''; + + this.exec.call(this, null, null, this.__pgmId, 'resizeMap', []); +}; + +Map.prototype.getMap = function(meta, div, options) { + var self = this, + args = [meta]; + options = options || {}; + + self.set('clickable', options.clickable === false ? false : true); + self.set('visible', options.visible === false ? false : true); + + if (options.controls) { + this.set('myLocation', options.controls.myLocation === true); + this.set('myLocationButton', options.controls.myLocationButton === true); + } + + if (options.preferences && options.preferences.gestureBounds) { + if (utils.isArray(options.preferences.gestureBounds) || + options.preferences.gestureBounds.type === 'LatLngBounds') { + options.preferences.gestureBounds = common.convertToPositionArray(options.preferences.gestureBounds); + } + } + + if (!common.isDom(div)) { + self.set('visible', false); + options = div; + options = options || {}; + if (options.camera) { + if (options.camera.latLng) { + options.camera.target = options.camera.latLng; + delete options.camera.latLng; + } + this.set('camera', options.camera); + if (options.camera.target) { + this.set('camera_target', options.camera.target); + } + if (options.camera.bearing) { + this.set('camera_bearing', options.camera.bearing); + } + if (options.camera.zoom) { + this.set('camera_zoom', options.camera.zoom); + } + if (options.camera.tilt) { + this.set('camera_tilt', options.camera.tilt); + } + } + args.push(options); + } else { + + var positionCSS = common.getStyle(div, 'position'); + if (!positionCSS || positionCSS === 'static') { + // important for HtmlInfoWindow + div.style.position = 'relative'; + } + options = options || {}; + if (options.camera) { + if (options.camera.latLng) { + options.camera.target = options.camera.latLng; + delete options.camera.latLng; + } + this.set('camera', options.camera); + if (options.camera.target) { + this.set('camera_target', options.camera.target); + } + if (options.camera.bearing) { + this.set('camera_bearing', options.camera.bearing); + } + if (options.camera.zoom) { + this.set('camera_zoom', options.camera.zoom); + } + if (options.camera.tilt) { + this.set('camera_tilt', options.camera.tilt); + } + } + if (utils.isArray(options.styles)) { + options.styles = JSON.stringify(options.styles); + } + args.push(options); + + div.style.overflow = 'hidden'; + self.set('div', div); + + if (div.offsetWidth < 100 || div.offsetHeight < 100) { + // If the map Div is too small, wait a little. + var callee = arguments.callee; + setTimeout(function() { + callee.call(self, meta, div, options); + }, 250 + Math.random() * 100); + return; + } + + // Gets the map div size. + // The plugin needs to consider the viewport zoom ratio + // for the case window.innerHTML > body.offsetWidth. + var elemId = common.getPluginDomId(div); + args.push(elemId); + + } + + exec.call({ + _isReady: true + }, function() { + + //------------------------------------------------------------------------ + // Clear background colors of map div parents after the map is created + //------------------------------------------------------------------------ + var div = self.get('div'); + if (common.isDom(div)) { + + // Insert the infoWindow layer + if (self._layers.info.parentNode) { + try { + self._layers.info.parentNode.removeChild(self._layers.info.parentNode); + } catch (e) { + // ignore + } + } + var positionCSS; + for (var i = 0; i < div.children.length; i++) { + positionCSS = common.getStyle(div.children[i], 'position'); + if (positionCSS === 'static') { + div.children[i].style.position = 'relative'; + } + } + div.insertBefore(self._layers.info, div.firstChild); + + + while (div.parentNode) { + div.style.backgroundColor = 'rgba(0,0,0,0) !important'; + + // Add _gmaps_cdv_ class + common.attachTransparentClass(div); + + div = div.parentNode; + } + } + cordova.fireDocumentEvent('plugin_touch', { + force: true + }); + + //------------------------------------------------------------------------ + // In order to work map.getVisibleRegion() correctly, wait a little. + //------------------------------------------------------------------------ + var waitCnt = 0; + var waitCameraSync = function() { + if (!self.getVisibleRegion() && (waitCnt++ < 10)) { + setTimeout(function() { + common.nextTick(waitCameraSync); + }, 100); + return; + } + + + self._privateInitialize(); + delete self._privateInitialize; + self.refreshLayout(); + self.trigger(event.MAP_READY, self); + }; + setTimeout(function() { + common.nextTick(waitCameraSync); + }, 100); + }, self.errorHandler, 'CordovaGoogleMaps', 'getMap', args, { + sync: true + }); +}; + +Map.prototype.setOptions = function(options) { + options = options || {}; + + if (options.controls) { + var myLocation = this.get('myLocation'); + if ('myLocation' in options.controls) { + myLocation = options.controls.myLocation === true; + } + var myLocationButton = this.get('myLocationButton'); + if ('myLocationButton' in options.controls) { + myLocationButton = options.controls.myLocationButton === true; + } + this.set('myLocation', myLocation); + this.set('myLocationButton', myLocationButton); + if (myLocation === true || myLocation === false) { + options.controls.myLocation = myLocation; + } + if (myLocationButton === true || myLocationButton === false) { + options.controls.myLocationButton = myLocationButton; + } + } + if (options.camera) { + if (options.camera.latLng) { + options.camera.target = options.camera.latLng; + delete options.camera.latLng; + } + this.set('camera', options.camera); + if (options.camera.target) { + this.set('camera_target', options.camera.target); + } + if (options.camera.bearing) { + this.set('camera_bearing', options.camera.bearing); + } + if (options.camera.zoom) { + this.set('camera_zoom', options.camera.zoom); + } + if (options.camera.tilt) { + this.set('camera_tilt', options.camera.tilt); + } + } + + if (options.preferences && options.preferences.gestureBounds) { + if (utils.isArray(options.preferences.gestureBounds) || + options.preferences.gestureBounds.type === 'LatLngBounds') { + options.preferences.gestureBounds = common.convertToPositionArray(options.preferences.gestureBounds); + } + } + + if (utils.isArray(options.styles)) { + options.styles = JSON.stringify(options.styles); + } + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setOptions', [options]); + return this; +}; + +Map.prototype.getMyLocation = function(params, success_callback, error_callback) { + return window.plugin.google.maps.LocationService.getMyLocation.call(this, params, success_callback, error_callback); +}; + +Map.prototype.setCameraTarget = function(latLng) { + this.set('camera_target', latLng); + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setCameraTarget', [latLng.lat, latLng.lng]); + return this; +}; + +Map.prototype.setCameraZoom = function(zoom) { + this.set('camera_zoom', zoom); + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setCameraZoom', [zoom], { + sync: true + }); + return this; +}; +Map.prototype.panBy = function(x, y) { + x = parseInt(x, 10); + y = parseInt(y, 10); + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'panBy', [x, y], { + sync: true + }); + return this; +}; + +/** + * Clears all markup that has been added to the map, + * including markers, polylines and ground overlays. + */ +Map.prototype.clear = function(callback) { + var self = this; + if (self._isRemoved) { + // Simply ignore because this map is already removed. + return Promise.resolve(); + } + + // Close the active infoWindow + var active_marker = self.get('active_marker'); + if (active_marker) { + active_marker.trigger(event.INFO_CLOSE); + } + + var clearObj = function(obj) { + var ids = Object.keys(obj); + var id, instance; + for (var i = 0; i < ids.length; i++) { + id = ids[i]; + instance = obj[id]; + if (instance) { + if (typeof instance.remove === 'function') { + instance.remove(); + } + instance.off(); + delete obj[id]; + } + } + obj = {}; + }; + + clearObj(self.OVERLAYS); + clearObj(self.MARKERS); + self.trigger('map_clear'); + + var resolver = function(resolve, reject) { + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + self.__pgmId, 'clear', [], { + sync: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + +}; + +/** + * @desc Change the map type + * @param {String} mapTypeId Specifies the one of the follow strings: + * MAP_TYPE_HYBRID + * MAP_TYPE_SATELLITE + * MAP_TYPE_TERRAIN + * MAP_TYPE_NORMAL + * MAP_TYPE_NONE + */ +Map.prototype.setMapTypeId = function(mapTypeId) { + if (mapTypeId !== MapTypeId[mapTypeId.replace('MAP_TYPE_', '')]) { + return this.errorHandler('Invalid MapTypeId was specified.'); + } + this.set('mapTypeId', mapTypeId); + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setMapTypeId', [mapTypeId]); + return this; +}; + +/** + * @desc Change the map view angle + * @param {Number} tilt The angle + */ +Map.prototype.setCameraTilt = function(tilt) { + this.set('camera_tilt', tilt); + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setCameraTilt', [tilt], { + sync: true + }); + return this; +}; + +/** + * @desc Change the map view bearing + * @param {Number} bearing The bearing + */ +Map.prototype.setCameraBearing = function(bearing) { + this.set('camera_bearing', bearing); + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setCameraBearing', [bearing], { + sync: true + }); + return this; +}; + +Map.prototype.moveCameraZoomIn = function(callback) { + var self = this; + var cameraPosition = self.get('camera'); + cameraPosition.zoom++; + cameraPosition.zoom = cameraPosition.zoom < 0 ? 0 : cameraPosition.zoom; + + return self.moveCamera(cameraPosition, callback); + +}; +Map.prototype.moveCameraZoomOut = function(callback) { + var self = this; + var cameraPosition = self.get('camera'); + cameraPosition.zoom--; + cameraPosition.zoom = cameraPosition.zoom < 0 ? 0 : cameraPosition.zoom; + + return self.moveCamera(cameraPosition, callback); +}; +Map.prototype.animateCameraZoomIn = function(callback) { + var self = this; + var cameraPosition = self.get('camera'); + cameraPosition.zoom++; + cameraPosition.zoom = cameraPosition.zoom < 0 ? 0 : cameraPosition.zoom; + cameraPosition.duration = 500; + return self.animateCamera(cameraPosition, callback); +}; +Map.prototype.animateCameraZoomOut = function(callback) { + var self = this; + var cameraPosition = self.get('camera'); + cameraPosition.zoom--; + cameraPosition.zoom = cameraPosition.zoom < 0 ? 0 : cameraPosition.zoom; + cameraPosition.duration = 500; + return self.animateCamera(cameraPosition, callback); +}; +/** + * Move the map camera with animation + * @name animateCamera + * @param {CameraPosition} cameraPosition - New camera position + * @param {Function} [callback] - This callback is involved when the animation is completed. + * @return {Promise} if you omit `callback`. + */ +Map.prototype.animateCamera = function(cameraPosition, callback) { + var self = this, + error; + + var target = cameraPosition.target; + if (!target && 'position' in cameraPosition) { + target = cameraPosition.position; + } + if (!target) { + error = new Error('No target field is specified.'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + if ('heading' in cameraPosition) { + cameraPosition.heading = cameraPosition.heading % 360; + } + if ('tilt' in cameraPosition) { + cameraPosition.tilt = Math.min(Math.max(0, cameraPosition.tilt), 90); + } + // if (!('padding' in cameraPosition)) { + // cameraPosition.padding = 10; + // } + + if (utils.isArray(target) || target.type === 'LatLngBounds') { + target = common.convertToPositionArray(target); + if (target.length === 0) { + // skip if no point is specified + error = new Error('No point is specified.'); + if (typeof callback === 'function') { + throw error; + } else { + return Promise.reject(error); + } + } + } + cameraPosition.target = target; + + var resolver = function(resolve, reject) { + + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + self.__pgmId, 'animateCamera', [cameraPosition], { + sync: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } +}; +/** + * Move the map camera without animation + * + * @name moveCamera + * @param {CameraPosition} - cameraPosition New camera position + * @param {Function} [callback] - This callback is involved when the animation is completed. + * @return {Promise} if you omit `callback`. + */ +Map.prototype.moveCamera = function(cameraPosition, callback) { + var self = this; + var target = cameraPosition.target; + if (!target && 'position' in cameraPosition) { + target = cameraPosition.position; + } + if (!target) { + return Promise.reject('No target field is specified.'); + } + + // if (!('padding' in cameraPosition)) { + // cameraPosition.padding = 10; + // } + if (utils.isArray(target) || target.type === 'LatLngBounds') { + target = common.convertToPositionArray(target); + if (target.length === 0) { + // skip if no point is specified + if (typeof callback === 'function') { + callback.call(self); + return; + } else { + return Promise.reject('No point is specified.'); + } + } + } + cameraPosition.target = target; + + var resolver = function(resolve, reject) { + + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + self.__pgmId, 'moveCamera', [cameraPosition], { + sync: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } +}; + +Map.prototype.setMyLocationButtonEnabled = function(enabled) { + var self = this; + enabled = common.parseBoolean(enabled); + this.set('myLocationButton', enabled); + self.exec.call(self, null, this.errorHandler, this.__pgmId, 'setMyLocationEnabled', [{ + myLocationButton: enabled, + myLocation: self.get('myLocation') === true + }], { + sync: true + }); + return this; +}; + +Map.prototype.setMyLocationEnabled = function(enabled) { + var self = this; + enabled = common.parseBoolean(enabled); + this.set('myLocation', enabled); + self.exec.call(self, null, this.errorHandler, this.__pgmId, 'setMyLocationEnabled', [{ + myLocationButton: self.get('myLocationButton') === true, + myLocation: enabled + }], { + sync: true + }); + return this; +}; + +Map.prototype.setIndoorEnabled = function(enabled) { + enabled = common.parseBoolean(enabled); + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setIndoorEnabled', [enabled]); + return this; +}; +Map.prototype.setTrafficEnabled = function(enabled) { + enabled = common.parseBoolean(enabled); + this.exec.call(this, null, this.errorHandler, this.__pgmId, 'setTrafficEnabled', [enabled]); + return this; +}; +Map.prototype.setCompassEnabled = function(enabled) { + var self = this; + enabled = common.parseBoolean(enabled); + self.exec.call(self, null, self.errorHandler, this.__pgmId, 'setCompassEnabled', [enabled]); + return this; +}; +Map.prototype.getFocusedBuilding = function(callback) { + var self = this; + var resolver = function(resolve, reject) { + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + self.__pgmId, 'getFocusedBuilding', []); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } +}; +Map.prototype.getVisible = function() { + return this.get('visible'); +}; +Map.prototype.setVisible = function(isVisible) { + cordova.fireDocumentEvent('plugin_touch'); + var self = this; + isVisible = common.parseBoolean(isVisible); + self.set('visible', isVisible); + self.exec.call(self, null, self.errorHandler, this.__pgmId, 'setVisible', [isVisible]); + return this; +}; + +Map.prototype.setClickable = function(isClickable) { + cordova.fireDocumentEvent('plugin_touch'); + var self = this; + isClickable = common.parseBoolean(isClickable); + self.set('clickable', isClickable); + self.exec.call(self, null, self.errorHandler, this.__pgmId, 'setClickable', [isClickable]); + return this; +}; +Map.prototype.getClickable = function() { + return this.get('clickable'); +}; + + +/** + * Sets the preference for whether all gestures should be enabled or disabled. + */ +Map.prototype.setAllGesturesEnabled = function(enabled) { + var self = this; + enabled = common.parseBoolean(enabled); + self.exec.call(self, null, self.errorHandler, this.__pgmId, 'setAllGesturesEnabled', [enabled]); + return this; +}; + +/** + * Return the current position of the camera + * @return {CameraPosition} + */ +Map.prototype.getCameraPosition = function() { + return this.get('camera'); +}; + +/** + * Cancel the camera animation + * @return {CameraPosition} + */ +Map.prototype.stopAnimation = function() { + var self = this; + if (self._isReady) { + cordova_exec(null, null, self.__pgmId, 'stopAnimation', []); + } +}; + +/** + * Remove the map completely. + */ +Map.prototype.remove = function(callback) { + var self = this; + if (self._isRemoved) { + return; + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + self.stopAnimation(); + + self.trigger('remove'); + // var div = self.get('div'); + // if (div) { + // while (div) { + // if (div.style) { + // div.style.backgroundColor = ''; + // } + // if (div.classList) { + // div.classList.remove('_gmaps_cdv_'); + // } else if (div.className) { + // div.className = div.className.replace(/_gmaps_cdv_/g, ''); + // div.className = div.className.replace(/\s+/g, ' '); + // } + // div = div.parentNode; + // } + // } + // self.set('div', undefined); + + + // Close the active infoWindow + var active_marker = self.get('active_marker'); + if (active_marker) { + active_marker.trigger(event.INFO_CLOSE); + } + + var clearObj = function(obj) { + var ids = Object.keys(obj); + var id, instance; + for (var i = 0; i < ids.length; i++) { + id = ids[i]; + instance = obj[id]; + if (instance) { + if (typeof instance.remove === 'function') { + instance.remove(); + } + instance.off(); + delete obj[id]; + } + } + obj = {}; + }; + + clearObj(self.OVERLAYS); + clearObj(self.MARKERS); + + + var resolver = function(resolve, reject) { + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + 'CordovaGoogleMaps', 'removeMap', [self.__pgmId], + { + sync: true, + remove: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } +}; + + +Map.prototype.toDataURL = function(params, callback) { + var args = [params || {}, callback]; + if (typeof args[0] === 'function') { + args.unshift({}); + } + + params = args[0]; + callback = args[1]; + + params.uncompress = params.uncompress === true; + var self = this; + + var resolver = function(resolve, reject) { + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + self.__pgmId, 'toDataURL', [params]); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } +}; + +/** + * Show the map into the specified div. + */ +Map.prototype.getDiv = function() { + return this.get('div'); +}; + +/** + * Show the map into the specified div. + */ +Map.prototype.setDiv = function(div) { + var self = this, + args = []; + + if (!common.isDom(div)) { + div = self.get('div'); + if (common.isDom(div)) { + div.removeAttribute('__pluginMapId'); + } + self.set('div', null); + } else { + div.setAttribute('__pluginMapId', self.__pgmId); + + // Insert the infoWindow layer + if (self._layers.info.parentNode) { + try { + self._layers.info.parentNode.removeChild(self._layers.info.parentNode); + } catch(e) { + //ignore + } + } + var positionCSS; + for (var i = 0; i < div.children.length; i++) { + positionCSS = common.getStyle(div.children[i], 'position'); + if (positionCSS === 'static') { + div.children[i].style.position = 'relative'; + } + } + div.insertBefore(self._layers.info, div.firstChild); + + // Webkit redraw mandatory + // http://stackoverflow.com/a/3485654/697856 + // div.style.display = 'none'; + // div.offsetHeight; + // div.style.display = ''; + document.body.style.transform = 'rotateZ(0deg)'; + + self.set('div', div); + + if (cordova.platform === 'browser') { + return; + } + + + positionCSS = common.getStyle(div, 'position'); + if (!positionCSS || positionCSS === 'static') { + div.style.position = 'relative'; + } + var elemId = common.getPluginDomId(div); + args.push(elemId); + while (div.parentNode) { + div.style.backgroundColor = 'rgba(0,0,0,0)'; + + // Add _gmaps_cdv_ class + common.attachTransparentClass(div); + + div = div.parentNode; + } + } + self.exec.call(self, function() { + cordova.fireDocumentEvent('plugin_touch', { + force: true, + action: 'setDiv' + }); + self.refreshLayout(); + }, self.errorHandler, self.__pgmId, 'setDiv', args, { + sync: true + }); + return self; +}; + +/** + * Return the visible region of the map. + */ +Map.prototype.getVisibleRegion = function(callback) { + var self = this; + var cameraPosition = self.get('camera'); + if (!cameraPosition || !cameraPosition.southwest || !cameraPosition.northeast) { + return null; + } + + var latLngBounds = new VisibleRegion( + cameraPosition.southwest, + cameraPosition.northeast, + cameraPosition.farLeft, + cameraPosition.farRight, + cameraPosition.nearLeft, + cameraPosition.nearRight + ); + + if (typeof callback === 'function') { + console.log('[deprecated] getVisibleRegion() is changed. Please check out the https://goo.gl/yHstHQ'); + callback.call(self, latLngBounds); + } + return latLngBounds; +}; + +/** + * Maps an Earth coordinate to a point coordinate in the map's view. + */ +Map.prototype.fromLatLngToPoint = function(latLng, callback) { + var self = this; + + if ('lat' in latLng && 'lng' in latLng) { + + var resolver = function(resolve, reject) { + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + self.__pgmId, 'fromLatLngToPoint', [latLng.lat, latLng.lng]); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + } else { + var rejector = function(resolve, reject) { + reject('The latLng is invalid'); + }; + + if (typeof callback === 'function') { + rejector(callback, self.errorHandler); + } else { + return new Promise(rejector); + } + } + +}; +/** + * Maps a point coordinate in the map's view to an Earth coordinate. + */ +Map.prototype.fromPointToLatLng = function(pixel, callback) { + var self = this; + if (typeof pixel === 'object' && 'x' in pixel && 'y' in pixel) { + pixel = [pixel.x, pixel.y]; + } + if (pixel.length == 2 && utils.isArray(pixel)) { + + var resolver = function(resolve, reject) { + self.exec.call(self, + function(result) { + var latLng = new LatLng(result[0] || 0, result[1] || 0); + resolve.call(self, latLng); + }, + reject.bind(self), + self.__pgmId, 'fromPointToLatLng', [pixel[0], pixel[1]]); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + } else { + var rejector = function(resolve, reject) { + reject('The pixel[] argument is invalid'); + }; + + if (typeof callback === 'function') { + rejector(callback, self.errorHandler); + } else { + return new Promise(rejector); + } + } + +}; + +Map.prototype.setPadding = function(p1, p2, p3, p4) { + if (arguments.length === 0 || arguments.length > 4) { + return this; + } + var padding = {}; + padding.top = parseInt(p1, 10); + switch (arguments.length) { + case 4: + // top right bottom left + padding.right = parseInt(p2, 10); + padding.bottom = parseInt(p3, 10); + padding.left = parseInt(p4, 10); + break; + + case 3: + // top right&left bottom + padding.right = parseInt(p2, 10); + padding.left = padding.right; + padding.bottom = parseInt(p3, 10); + break; + + case 2: + // top & bottom right&left + padding.bottom = parseInt(p1, 10); + padding.right = parseInt(p2, 10); + padding.left = padding.right; + break; + + case 1: + // top & bottom right & left + padding.bottom = padding.top; + padding.right = padding.top; + padding.left = padding.top; + break; + } + this.exec.call(this, null, self.errorHandler, this.__pgmId, 'setPadding', [padding]); + return this; +}; + + +Map.prototype.addKmlOverlay = function(kmlOverlayOptions, callback) { + var self = this; + kmlOverlayOptions = kmlOverlayOptions || {}; + kmlOverlayOptions.url = kmlOverlayOptions.url || null; + kmlOverlayOptions.clickable = common.defaultTrueOption(kmlOverlayOptions.clickable); + kmlOverlayOptions.suppressInfoWindows = kmlOverlayOptions.suppressInfoWindows === true; + + if (kmlOverlayOptions.url) { + + var link = document.createElement('a'); + link.href = kmlOverlayOptions.url; + kmlOverlayOptions.url = link.protocol+'//'+link.host+link.pathname + link.search; + + var invisible_dot = self.get('invisible_dot'); + if (!invisible_dot || invisible_dot._isRemoved) { + // Create an invisible marker for kmlOverlay + self.set('invisible_dot', self.addMarker({ + position: { + lat: 0, + lng: 0 + }, + icon: 'skyblue', + visible: false + })); + } + if ('icon' in kmlOverlayOptions) { + self.get('invisible_dot').setIcon(kmlOverlayOptions.icon); + } + + var resolver = function(resolve, reject) { + + var loader = new KmlLoader(self, self.exec, kmlOverlayOptions); + loader.parseKmlFile(function(camera, kmlData) { + if (kmlData instanceof BaseClass) { + kmlData = new BaseArrayClass([kmlData]); + } + var kmlId = 'kmloverlay_' + Math.floor(Math.random() * Date.now()); + var kmlOverlay = new KmlOverlay(self, kmlId, camera, kmlData, kmlOverlayOptions); + self.OVERLAYS[kmlId] = kmlOverlay; + resolve.call(self, kmlOverlay); + }, reject); + + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + } else { + + if (typeof callback === 'function') { + throw new Error('KML file url is required.'); + } else { + return Promise.reject('KML file url is required.'); + } + } +}; + + +//------------- +// Ground overlay +//------------- +Map.prototype.addGroundOverlay = function(groundOverlayOptions, callback) { + var self = this; + groundOverlayOptions = groundOverlayOptions || {}; + groundOverlayOptions.anchor = groundOverlayOptions.anchor || [0.5, 0.5]; + groundOverlayOptions.bearing = 'bearing' in groundOverlayOptions ? groundOverlayOptions.bearing : 0; + groundOverlayOptions.url = groundOverlayOptions.url || null; + groundOverlayOptions.clickable = groundOverlayOptions.clickable === true; + groundOverlayOptions.visible = common.defaultTrueOption(groundOverlayOptions.visible); + groundOverlayOptions.zIndex = groundOverlayOptions.zIndex || 0; + groundOverlayOptions.bounds = common.convertToPositionArray(groundOverlayOptions.bounds); + groundOverlayOptions.noCaching = true; + + var groundOverlay = new GroundOverlay(self, groundOverlayOptions, exec); + var groundOverlayId = groundOverlay.getId(); + self.OVERLAYS[groundOverlayId] = groundOverlay; + groundOverlay.one(groundOverlayId + '_remove', function() { + groundOverlay.off(); + delete self.OVERLAYS[groundOverlayId]; + groundOverlay = undefined; + }); + + self.exec.call(self, function() { + groundOverlay._privateInitialize(); + delete groundOverlay._privateInitialize; + if (typeof callback === 'function') { + callback.call(self, groundOverlay); + } + }, self.errorHandler, self.__pgmId, 'loadPlugin', ['GroundOverlay', groundOverlayOptions, groundOverlay.hashCode]); + + return groundOverlay; +}; + +//------------- +// Tile overlay +//------------- +Map.prototype.addTileOverlay = function(tilelayerOptions, callback) { + var self = this; + tilelayerOptions = tilelayerOptions || {}; + tilelayerOptions.tileUrlFormat = tilelayerOptions.tileUrlFormat || null; + if (typeof tilelayerOptions.tileUrlFormat === 'string') { + console.log('[deprecated] the tileUrlFormat property is now deprecated. Use the getTile property.'); + tilelayerOptions.getTile = function(x, y, zoom) { + return tilelayerOptions.tileUrlFormat.replace(//gi, x) + .replace(//gi, y) + .replace(//gi, zoom); + }; + } + if (typeof tilelayerOptions.getTile !== 'function') { + throw new Error('[error] the getTile property is required.'); + } + tilelayerOptions.visible = common.defaultTrueOption(tilelayerOptions.visible); + tilelayerOptions.zIndex = tilelayerOptions.zIndex || 0; + tilelayerOptions.tileSize = tilelayerOptions.tileSize || 512; + tilelayerOptions.opacity = (tilelayerOptions.opacity === null || tilelayerOptions.opacity === undefined) ? 1 : tilelayerOptions.opacity; + tilelayerOptions.debug = tilelayerOptions.debug === true; + tilelayerOptions.userAgent = tilelayerOptions.userAgent || navigator.userAgent; + + + var tileOverlay = new TileOverlay(self, tilelayerOptions, exec); + var tileOverlayId = tileOverlay.getId(); + self.OVERLAYS[tileOverlayId] = tileOverlay; + var hashCode = tileOverlay.hashCode; + + tileOverlay.one(tileOverlayId + '_remove', function() { + document.removeEventListener(tileOverlayId + '-' + hashCode + '-tileoverlay', onNativeCallback); + tileOverlay.off(); + delete self.OVERLAYS[tileOverlayId]; + tileOverlay = undefined; + }); + + var options = { + visible: tilelayerOptions.visible, + zIndex: tilelayerOptions.zIndex, + tileSize: tilelayerOptions.tileSize, + opacity: tilelayerOptions.opacity, + userAgent: tilelayerOptions.userAgent, + debug: tilelayerOptions.debug + }; + + var onNativeCallback = function(params) { + var url = tilelayerOptions.getTile(params.x, params.y, params.zoom); + if (!url || url === '(null)' || url === 'undefined' || url === 'null') { + url = '(null)'; + } + if (url instanceof Promise) { + common.promiseTimeout(5000, url) + .then(function(finalUrl) { + + var link = document.createElement('a'); + link.href = finalUrl; + finalUrl = link.protocol+'//'+link.host+link.pathname + link.search; + + cordova_exec(null, self.errorHandler, self.__pgmId + '-tileoverlay', 'onGetTileUrlFromJS', [hashCode, params.key, finalUrl]); + }) + .catch(function() { + cordova_exec(null, self.errorHandler, self.__pgmId + '-tileoverlay', 'onGetTileUrlFromJS', [hashCode, params.key, '(null)']); + }); + } else { + + var link = document.createElement('a'); + link.href = url; + url = link.protocol+'//'+link.host+link.pathname + link.search; + cordova_exec(null, self.errorHandler, self.__pgmId + '-tileoverlay', 'onGetTileUrlFromJS', [hashCode, params.key, url]); + } + }; + document.addEventListener(self.__pgmId + '-' + hashCode + '-tileoverlay', onNativeCallback); + + self.exec.call(self, function() { + tileOverlay._privateInitialize(); + delete tileOverlay._privateInitialize; + + if (typeof callback === 'function') { + callback.call(self, tileOverlay); + } + }, self.errorHandler, self.__pgmId, 'loadPlugin', ['TileOverlay', options, hashCode]); + + return tileOverlay; +}; + +//------------- +// Polygon +//------------- +Map.prototype.addPolygon = function(polygonOptions, callback) { + var self = this; + polygonOptions.points = polygonOptions.points || []; + var _orgs = polygonOptions.points; + polygonOptions.points = common.convertToPositionArray(polygonOptions.points); + polygonOptions.holes = polygonOptions.holes || []; + if (polygonOptions.holes.length > 0 && !Array.isArray(polygonOptions.holes[0])) { + polygonOptions.holes = [polygonOptions.holes]; + } + polygonOptions.holes = polygonOptions.holes.map(function(hole) { + if (!utils.isArray(hole)) { + return []; + } + return hole.map(function(position) { + return { + 'lat': position.lat, + 'lng': position.lng + }; + }); + }); + polygonOptions.strokeColor = common.HTMLColor2RGBA(polygonOptions.strokeColor || '#FF000080', 0.75); + if (polygonOptions.fillColor) { + polygonOptions.fillColor = common.HTMLColor2RGBA(polygonOptions.fillColor || '#FF000080', 0.75); + } else { + polygonOptions.fillColor = common.HTMLColor2RGBA('#FF000080', 0.75); + } + polygonOptions.strokeWidth = 'strokeWidth' in polygonOptions ? polygonOptions.strokeWidth : 10; + polygonOptions.visible = common.defaultTrueOption(polygonOptions.visible); + polygonOptions.clickable = polygonOptions.clickable === true; + polygonOptions.zIndex = polygonOptions.zIndex || 0; + polygonOptions.geodesic = polygonOptions.geodesic === true; + + var opts = JSON.parse(JSON.stringify(polygonOptions)); + polygonOptions.points = _orgs; + var polygon = new Polygon(self, polygonOptions, exec); + var polygonId = polygon.getId(); + self.OVERLAYS[polygonId] = polygon; + polygon.one(polygonId + '_remove', function() { + polygon.off(); + delete self.OVERLAYS[polygonId]; + polygon = undefined; + }); + + self.exec.call(self, function() { + polygon._privateInitialize(); + delete polygon._privateInitialize; + + if (typeof callback === 'function') { + callback.call(self, polygon); + } + }, self.errorHandler, self.__pgmId, 'loadPlugin', ['Polygon', opts, polygon.hashCode]); + + return polygon; +}; + +//------------- +// Polyline +//------------- +Map.prototype.addPolyline = function(polylineOptions, callback) { + var self = this; + polylineOptions.points = polylineOptions.points || []; + var _orgs = polylineOptions.points; + polylineOptions.points = common.convertToPositionArray(polylineOptions.points); + polylineOptions.color = common.HTMLColor2RGBA(polylineOptions.color || '#FF000080', 0.75); + polylineOptions.width = 'width' in polylineOptions ? polylineOptions.width : 10; + polylineOptions.visible = common.defaultTrueOption(polylineOptions.visible); + polylineOptions.clickable = polylineOptions.clickable === true; + polylineOptions.zIndex = polylineOptions.zIndex || 0; + polylineOptions.geodesic = polylineOptions.geodesic === true; + + var opts = JSON.parse(JSON.stringify(polylineOptions)); + polylineOptions.points = _orgs; + var polyline = new Polyline(self, polylineOptions, exec); + var polylineId = polyline.getId(); + self.OVERLAYS[polylineId] = polyline; + + polyline.one(polylineId + '_remove', function() { + polyline.off(); + delete self.OVERLAYS[polylineId]; + polyline = undefined; + }); + + self.exec.call(self, function() { + if (polyline) { + polyline._privateInitialize(); + delete polyline._privateInitialize; + + if (typeof callback === 'function') { + callback.call(self, polyline); + } + } + }, self.errorHandler, self.__pgmId, 'loadPlugin', ['Polyline', opts, polyline.hashCode]); + + return polyline; +}; + +//------------- +// Circle +//------------- +Map.prototype.addCircle = function(circleOptions, callback) { + var self = this; + circleOptions.center = circleOptions.center || {}; + circleOptions.center.lat = circleOptions.center.lat || 0.0; + circleOptions.center.lng = circleOptions.center.lng || 0.0; + circleOptions.strokeColor = common.HTMLColor2RGBA(circleOptions.strokeColor || '#FF0000', 0.75); + circleOptions.fillColor = common.HTMLColor2RGBA(circleOptions.fillColor || '#000000', 0.75); + circleOptions.strokeWidth = 'strokeWidth' in circleOptions ? circleOptions.strokeWidth : 10; + circleOptions.visible = common.defaultTrueOption(circleOptions.visible); + circleOptions.zIndex = circleOptions.zIndex || 0; + circleOptions.radius = 'radius' in circleOptions ? circleOptions.radius : 1; + + var circle = new Circle(self, circleOptions, exec); + var circleId = circle.getId(); + self.OVERLAYS[circleId] = circle; + circle.one(circleId + '_remove', function() { + circle.off(); + delete self.OVERLAYS[circleId]; + circle = undefined; + }); + + self.exec.call(self, function() { + circle._privateInitialize(); + delete circle._privateInitialize; + + if (typeof callback === 'function') { + callback.call(self, circle); + } + }, self.errorHandler, self.__pgmId, 'loadPlugin', ['Circle', circleOptions, circle.hashCode]); + + return circle; +}; + +//------------- +// Marker +//------------- + +Map.prototype.addMarker = function(markerOptions, callback) { + var self = this; + markerOptions = common.markerOptionsFilter(markerOptions); + + //------------------------------------ + // Generate a makrer instance at once. + //------------------------------------ + markerOptions.icon = markerOptions.icon || {}; + var link; + if (typeof markerOptions.icon === 'string') { + if (markerOptions.icon.indexOf('://') === -1 && + markerOptions.icon.indexOf('.') === 0) { + + link = document.createElement('a'); + link.href = markerOptions.icon; + markerOptions.icon = link.protocol+'//'+link.host+link.pathname + link.search; + link = undefined; + } + } else if (typeof markerOptions.icon === 'object' && typeof markerOptions.icon.url === 'string') { + if (markerOptions.icon.url.indexOf('://') === -1 && + markerOptions.icon.url.indexOf('.') === 0) { + + link = document.createElement('a'); + link.href = markerOptions.icon.url; + markerOptions.icon.url = link.protocol+'//'+link.host+link.pathname + link.search; + link = undefined; + } + } + + var marker = new Marker(self, markerOptions, exec); + var markerId = marker.getId(); + + self.MARKERS[markerId] = marker; + self.OVERLAYS[markerId] = marker; + marker.one(markerId + '_remove', function() { + delete self.MARKERS[markerId]; + delete self.OVERLAYS[markerId]; + marker.destroy(); + marker = undefined; + }); + + if (typeof markerOptions.anchor === 'object' && + 'x' in markerOptions.anchor && 'y' in markerOptions.anchor) { + markerOptions.anchor = [markerOptions.anchor.x, markerOptions.anchor.y]; + } + + self.exec.call(self, function(result) { + if (marker) { + markerOptions.icon.size = markerOptions.icon.size || {}; + markerOptions.icon.size.width = markerOptions.icon.size.width || result.width; + markerOptions.icon.size.height = markerOptions.icon.size.height || result.height; + markerOptions.icon.anchor = markerOptions.icon.anchor || [markerOptions.icon.size.width / 2, markerOptions.icon.size.height]; + + if (!markerOptions.infoWindowAnchor) { + markerOptions.infoWindowAnchor = [markerOptions.icon.size.width / 2, 0]; + } + marker._privateInitialize(markerOptions); + delete marker._privateInitialize; + + if (typeof callback === 'function') { + callback.call(self, marker); + } + } + }, self.errorHandler, self.__pgmId, 'loadPlugin', ['Marker', markerOptions, marker.hashCode]); + + return marker; +}; + + +//------------------ +// Marker cluster +//------------------ +Map.prototype.addMarkerCluster = function(markerClusterOptions, callback) { + var self = this; + if (typeof markerClusterOptions === 'function') { + callback = markerClusterOptions; + markerClusterOptions = null; + } + markerClusterOptions = markerClusterOptions || {}; + var positionList = markerClusterOptions.markers.map(function(marker) { + return marker.position; + }); + + var markerCluster = new MarkerCluster(self, { + 'icons': markerClusterOptions.icons, + //'markerMap': markerMap, + 'idxCount': positionList.length + 1, + 'maxZoomLevel': Math.min(markerClusterOptions.maxZoomLevel || 15, 18), + 'debug': markerClusterOptions.debug === true, + 'boundsDraw': common.defaultTrueOption(markerClusterOptions.boundsDraw) + }, exec); + var markerClusterId = markerCluster.getId(); + self.OVERLAYS[markerClusterId] = markerCluster; + + self.exec.call(self, function(result) { + + result.geocellList.forEach(function(geocell, idx) { + var markerOptions = markerClusterOptions.markers[idx]; + markerOptions = common.markerOptionsFilter(markerOptions); + + markerOptions._cluster = { + isRemoved: false, + isAdded: false, + geocell: geocell + }; + markerCluster.addMarker(markerOptions); + + //self.MARKERS[marker.getId()] = marker; + //self.OVERLAYS[marker.getId()] = marker; + }); + + + markerCluster.one('remove', function() { + delete self.OVERLAYS[result.__pgmId]; + /* + result.geocellList.forEach(function(geocell, idx) { + var markerOptions = markerClusterOptions.markers[idx]; + var markerId = result.__pgmId + '-' + (markerOptions.__pgmId || 'marker_' + idx); + var marker = self.MARKERS[markerId]; + if (marker) { + marker.off(); + } + //delete self.MARKERS[markerId]; + delete self.OVERLAYS[markerId]; + }); + */ + markerCluster.destroy(); + }); + + markerCluster._privateInitialize(); + delete markerCluster._privateInitialize; + + markerCluster._triggerRedraw.call(markerCluster, { + force: true + }); + + if (typeof callback === 'function') { + callback.call(self, markerCluster); + } + }, self.errorHandler, self.__pgmId, 'loadPlugin', ['MarkerCluster', { + 'positionList': positionList, + 'debug': markerClusterOptions.debug === true + }, markerCluster.hashCode]); + + return markerCluster; +}; + +/***************************************************************************** + * Callbacks from the native side + *****************************************************************************/ + +Map.prototype._onSyncInfoWndPosition = function(eventName, points) { + this.set('infoPosition', points); +}; + +Map.prototype._onMapEvent = function(eventName) { + if (!this._isReady) { + return; + } + var args = [eventName]; + for (var i = 1; i < arguments.length; i++) { + args.push(arguments[i]); + } + this.trigger.apply(this, args); +}; + +Map.prototype._onMarkerEvent = function(eventName, markerId, position) { + var self = this; + var marker = self.MARKERS[markerId] || null; + + if (marker) { + marker.set('position', position); + if (eventName === event.INFO_OPEN) { + marker.set('isInfoWindowVisible', true); + } + if (eventName === event.INFO_CLOSE) { + marker.set('isInfoWindowVisible', false); + } + marker.trigger(eventName, position, marker); + } +}; + +Map.prototype._onClusterEvent = function(eventName, markerClusterId, clusterId, position) { + var self = this; + var markerCluster = self.OVERLAYS[markerClusterId] || null; + if (markerCluster) { + if (/^marker_/i.test(clusterId)) { + // regular marker + var marker = markerCluster.getMarkerById(clusterId); + if (eventName === event.MARKER_CLICK) { + markerCluster.trigger(eventName, position, marker); + } else { + if (eventName === event.INFO_OPEN) { + marker.set('isInfoWindowVisible', true); + } + if (eventName === event.INFO_CLOSE) { + marker.set('isInfoWindowVisible', false); + } + } + marker.trigger(eventName, position, marker); + } else { + // cluster marker + var cluster = markerCluster._getClusterByClusterId(clusterId); + if (cluster) { + markerCluster.trigger(eventName, cluster); + } else { + console.log('-----> This is remained cluster icon : ' + clusterId); + } + } + } +}; + +Map.prototype._onOverlayEvent = function(eventName, overlayId) { + var self = this; + var overlay = self.OVERLAYS[overlayId] || null; + + if (overlay) { + var args = [eventName]; + for (var i = 2; i < arguments.length; i++) { + args.push(arguments[i]); + } + args.push(overlay); // for ionic + overlay.trigger.apply(overlay, args); + } +}; + +Map.prototype.getCameraTarget = function() { + return this.get('camera_target'); +}; + +Map.prototype.getCameraZoom = function() { + return this.get('camera_zoom'); +}; +Map.prototype.getCameraTilt = function() { + return this.get('camera_tilt'); +}; +Map.prototype.getCameraBearing = function() { + return this.get('camera_bearing'); +}; + +Map.prototype._onCameraEvent = function(eventName, cameraPosition) { + this.set('camera', cameraPosition); + this.set('camera_target', cameraPosition.target); + this.set('camera_zoom', cameraPosition.zoom); + this.set('camera_bearing', cameraPosition.bearing); + this.set('camera_tilt', cameraPosition.viewAngle || cameraPosition.tilt); + this.set('camera_northeast', cameraPosition.northeast); + this.set('camera_southwest', cameraPosition.southwest); + this.set('camera_nearLeft', cameraPosition.nearLeft); + this.set('camera_nearRight', cameraPosition.nearRight); + this.set('camera_farLeft', cameraPosition.farLeft); + this.set('camera_farRight', cameraPosition.farRight); + if (this._isReady) { + this.trigger(eventName, cameraPosition, this); + } +}; + +module.exports = Map; diff --git a/node_modules/cordova-plugin-googlemaps/www/MapTypeId.js b/node_modules/cordova-plugin-googlemaps/www/MapTypeId.js new file mode 100644 index 0000000..aa6862a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/MapTypeId.js @@ -0,0 +1,8 @@ +module.exports = { + 'NORMAL': 'MAP_TYPE_NORMAL', + 'ROADMAP': 'MAP_TYPE_NORMAL', + 'SATELLITE': 'MAP_TYPE_SATELLITE', + 'HYBRID': 'MAP_TYPE_HYBRID', + 'TERRAIN': 'MAP_TYPE_TERRAIN', + 'NONE': 'MAP_TYPE_NONE' +}; diff --git a/node_modules/cordova-plugin-googlemaps/www/Marker.js b/node_modules/cordova-plugin-googlemaps/www/Marker.js new file mode 100644 index 0000000..0fd70ca --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Marker.js @@ -0,0 +1,362 @@ + + +var utils = require('cordova/utils'), + common = require('./Common'), + LatLng = require('./LatLng'), + event = require('./event'), + Overlay = require('./Overlay'); + +/***************************************************************************** + * Marker Class + *****************************************************************************/ +var Marker = function(map, markerOptions, _exec, extras) { + extras = extras || {}; + Overlay.call(this, map, markerOptions, extras.className || 'Marker', _exec, extras); + + var self = this; + + if (markerOptions && markerOptions.position) { + markerOptions.position.lat = parseFloat(markerOptions.position.lat); + markerOptions.position.lng = parseFloat(markerOptions.position.lng); + self.set('position', markerOptions.position); + } + + //----------------------------------------------- + // Sets event listeners + //----------------------------------------------- + self.on(event.MARKER_CLICK, function() { + self.showInfoWindow.apply(self); + }); + + self.on('position_changed', function() { + var position = self.get('position'); + try { + position.lat = parseFloat(position.lat, 10); + position.lng = parseFloat(position.lng, 10); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setPosition', [self.getId(), position.lat, position.lng]); + } catch (e) { + return; + } + }); + self.on('rotation_changed', function() { + var rotation = self.get('rotation'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setRotation', [self.getId(), rotation]); + }); + self.on('snippet_changed', function() { + var snippet = self.get('snippet'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setSnippet', [self.getId(), snippet]); + }); + self.on('visible_changed', function() { + var visible = self.get('visible'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setVisible', [self.getId(), visible]); + }); + self.on('title_changed', function() { + var title = self.get('title'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setTitle', [self.getId(), title]); + }); + self.on('icon_changed', function() { + var icon = self.get('icon'); + + var link; + if (typeof icon === 'string') { + if (icon.indexOf('://') === -1 && + icon.indexOf('.') === 0) { + + link = document.createElement('a'); + link.href = icon; + icon = link.protocol+'//'+link.host+link.pathname + link.search; + link = undefined; + } + } else if (typeof icon === 'object' && typeof icon.url === 'string') { + if (icon.url.indexOf('://') === -1 && + icon.url.indexOf('.') === 0) { + + link = document.createElement('a'); + link.href = icon.url; + icon.url = link.protocol+'//'+link.host+link.pathname + link.search; + link = undefined; + } + } + + if (typeof icon === 'object' && + typeof icon.anchor === 'object' && + 'x' in icon.anchor && + 'y' in icon.anchor) { + icon.anchor = [icon.anchor.x, icon.anchor.y]; + } + + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setIcon', [self.getId(), icon]); + }); + self.on('flat_changed', function() { + var flat = self.get('flat'); + flat = flat === true; + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setFlat', [self.getId(), flat]); + }); + self.on('draggable_changed', function() { + var draggable = self.get('draggable'); + draggable = draggable === true; + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setDraggable', [self.getId(), draggable]); + }); + self.on('anchor_changed', function() { + var anchor = self.get('anchor'); + if (!anchor) { + return; + } + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setIconAnchor', [self.getId(), anchor[0], anchor[1]]); + }); + self.on('infoWindowAnchor_changed', function() { + var anchor = self.get('infoWindowAnchor'); + if (!anchor) { + return; + } + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setInfoWindowAnchor', [self.getId(), anchor[0], anchor[1]]); + }); + self.on('zIndex_changed', function() { + var zIndex = self.get('zIndex'); + if (zIndex === null || zIndex === undefined) { + return; + } + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setZIndex', [self.getId(), zIndex]); + }); + self.on('opacity_changed', function() { + var opacity = self.get('opacity'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setOpacity', [self.getId(), opacity]); + }); + self.on('disableAutoPan_changed', function() { + var disableAutoPan = self.get('disableAutoPan'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setDisableAutoPan', [self.getId(), disableAutoPan]); + }); + +}; + +utils.extend(Marker, Overlay); + +Marker.prototype.remove = function(callback) { + var self = this; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + self.trigger(event.INFO_CLOSE); // close open infowindow, otherwise it will stay + self.trigger(self.__pgmId + '_remove'); + + var resolver = function(resolve, reject) { + self.exec.call(self, + function() { + self.destroy(); + resolve.call(self); + }, + reject.bind(self), + self.getPluginName(), 'remove', [self.getId()], { + remove: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + + +}; + +Marker.prototype.getOptions = function() { + var self = this; + return { + '__pgmId': self.getId(), + 'position': self.getPosition(), + 'disableAutoPan': self.get('disableAutoPan'), + 'opacity': self.get('opacity'), + 'icon': self.get('icon'), + 'zIndex': self.get('zIndex'), + 'anchor': self.get('anchor'), + 'infoWindowAnchor': self.get('infoWindowAnchor'), + 'draggable': self.get('draggable'), + 'title': self.getTitle(), + 'snippet': self.getSnippet(), + 'visible': self.get('visible'), + 'rotation': self.getRotation() + }; +}; +Marker.prototype.getPosition = function() { + var position = this.get('position'); + if (!(position instanceof LatLng)) { + return new LatLng(position.lat, position.lng); + } + return position; +}; + +Marker.prototype.setAnimation = function(animation, callback) { + var self = this; + + animation = animation || null; + if (!animation) { + // just ignore + if (typeof callback === 'function') { + return self; + } else { + return Promise.resolve(); + } + } + self.set('animation', animation); + + var resolver = function(resolve, reject) { + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + self.getPluginName(), 'setAnimation', [self.getId(), animation]); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + return self; + } else { + return new Promise(resolver); + } + +}; + +Marker.prototype.setDisableAutoPan = function(disableAutoPan) { + disableAutoPan = common.parseBoolean(disableAutoPan); + this.set('disableAutoPan', disableAutoPan); + return this; +}; +Marker.prototype.setOpacity = function(opacity) { + if (!opacity && opacity !== 0) { + console.log('opacity value must be int or double'); + return false; + } + this.set('opacity', opacity); + return this; +}; +Marker.prototype.setZIndex = function(zIndex) { + if (typeof zIndex === 'undefined') { + return false; + } + this.set('zIndex', zIndex); + return this; +}; +Marker.prototype.getOpacity = function() { + return this.get('opacity'); +}; +Marker.prototype.setIconAnchor = function(anchorX, anchorY) { + this.set('anchor', [anchorX, anchorY]); + return this; +}; +Marker.prototype.setInfoWindowAnchor = function(anchorX, anchorY) { + this.set('infoWindowAnchor', [anchorX, anchorY]); + return this; +}; +Marker.prototype.setDraggable = function(draggable) { + draggable = common.parseBoolean(draggable); + this.set('draggable', draggable); + return this; +}; +Marker.prototype.isDraggable = function() { + return this.get('draggable'); +}; +Marker.prototype.setFlat = function(flat) { + flat = common.parseBoolean(flat); + this.set('flat', flat); + return this; +}; +Marker.prototype.setIcon = function(url) { + if (url && common.isHTMLColorString(url)) { + url = common.HTMLColor2RGBA(url); + } + this.set('icon', url); + return this; +}; +Marker.prototype.setTitle = function(title) { + if (!title) { + console.log('missing value for title'); + return this; + } + title = '' + title; // Convert to strings mandatory + this.set('title', title); + return this; +}; +Marker.prototype.setVisible = function(visible) { + visible = common.parseBoolean(visible); + this.set('visible', visible); + if (!visible && this.map.get('active_marker_id') === this.__pgmId) { + this.map.set('active_marker_id', undefined); + } + return this; +}; +Marker.prototype.getTitle = function() { + return this.get('title'); +}; +Marker.prototype.setSnippet = function(snippet) { + this.set('snippet', snippet); + return this; +}; +Marker.prototype.getSnippet = function() { + return this.get('snippet'); +}; +Marker.prototype.setRotation = function(rotation) { + if (typeof rotation !== 'number') { + console.log('missing value for rotation'); + return false; + } + this.set('rotation', rotation); + return this; +}; +Marker.prototype.getRotation = function() { + return this.get('rotation'); +}; +Marker.prototype.showInfoWindow = function() { + var self = this; + //if (!self.get('title') && !self.get('snippet') || + // self.get('isInfoWindowVisible')) { + if (!self.get('title') && !self.get('snippet')) { + return; + } + self.set('isInfoWindowVisible', true); + self.map.set('active_marker_id', self.__pgmId); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'showInfoWindow', [self.getId()], { + sync: true + }); + return self; +}; +Marker.prototype.hideInfoWindow = function() { + var self = this; + if (self.map.get('active_marker_id') === self.__pgmId) { + self.map.set('active_marker_id', null); + } + if (self.get('isInfoWindowVisible')) { + self.set('isInfoWindowVisible', false); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'hideInfoWindow', [self.getId()], { + sync: true + }); + } + return self; +}; +Marker.prototype.isInfoWindowShown = function() { + return this.get('isInfoWindowVisible') === true; +}; +Marker.prototype.isVisible = function() { + return this.get('visible') === true; +}; + +Marker.prototype.setPosition = function(position) { + if (!position) { + console.log('missing value for position'); + return false; + } + this.set('position', { + 'lat': position.lat, + 'lng': position.lng + }); + return this; +}; + +module.exports = Marker; diff --git a/node_modules/cordova-plugin-googlemaps/www/MarkerCluster.js b/node_modules/cordova-plugin-googlemaps/www/MarkerCluster.js new file mode 100644 index 0000000..4f3cd9b --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/MarkerCluster.js @@ -0,0 +1,1240 @@ + +/* eslint no-useless-escape: off */ + +var utils = require('cordova/utils'), + common = require('./Common'), + event = require('./event'), + geomodel = require('./geomodel'), + Marker = require('./Marker'), + Cluster = require('./Cluster'), + spherical = require('./spherical'), + Overlay = require('./Overlay'), + BaseArrayClass = require('./BaseArrayClass'); + +/***************************************************************************** + * MarkerCluster Class + *****************************************************************************/ +var exec; +var MarkerCluster = function (map, markerClusterOptions, _exec) { + exec = _exec; + Overlay.call(this, map, markerClusterOptions, 'MarkerCluster', _exec); + + var self = this; + Object.defineProperty(self, 'maxZoomLevel', { + value: markerClusterOptions.maxZoomLevel, + writable: false + }); + Object.defineProperty(self, '_markerMap', { + enumerable: false, + value: {}, + writable: false + }); + Object.defineProperty(self, '_clusterBounds', { + enumerable: false, + value: new BaseArrayClass(), + writable: false + }); + Object.defineProperty(self, '_geocellBounds', { + enumerable: false, + value: {}, + writable: false + }); + Object.defineProperty(self, '_clusters', { + enumerable: false, + value: {}, + writable: false + }); + Object.defineProperty(self, 'debug', { + value: markerClusterOptions.debug === true, + writable: false + }); + Object.defineProperty(self, 'MAX_RESOLUTION', { + enumerable: false, + value: 11, + writable: false + }); + Object.defineProperty(self, 'OUT_OF_RESOLUTION', { + enumerable: false, + value: 999, + writable: false + }); + Object.defineProperty(self, 'boundsDraw', { + value: markerClusterOptions.boundsDraw === true, + writable: false + }); + + if (self.boundsDraw) { + self.map.addPolygon({ + visible: false, + points: [ + { + lat: 0, + lng: 0 + }, + { + lat: 0, + lng: 0 + }, + { + lat: 0, + lng: 0 + }, + { + lat: 0, + lng: 0 + } + ], + strokeWidth: 1 + }, function (polygon) { + self.set('polygon', polygon); + }); + } + self.taskQueue = []; + self._stopRequest = false; + self._isWorking = false; + + var icons = markerClusterOptions.icons; + if (icons.length > 0 && !icons[0].min) { + icons[0].min = 2; + } + + for (var i = 0; i < icons.length; i++) { + if (!icons[i]) { + continue; + } + var link; + if (typeof icons[i] === 'string') { + if (icons[i].indexOf('://') === -1 && + icons[i].indexOf('.') === 0) { + + link = document.createElement('a'); + link.href = icons[i]; + icons[i] = link.protocol + '//' + link.host + link.pathname + link.search; + link = undefined; + } + } else if (typeof icons[i].url === 'string') { + if (icons[i].url.indexOf('://') === -1 && + icons[i].url.indexOf('.') === 0) { + + link = document.createElement('a'); + link.href = icons[i].url; + icons[i].url = link.protocol + '//' + link.host + link.pathname + link.search; + link = undefined; + } + + } + if (icons[i].anchor && + typeof icons[i].anchor === 'object' && + 'x' in icons[i].anchor && + 'y' in icons[i].anchor) { + icons[i].anchor = [icons[i].anchor.x, icons[i].anchor.y]; + } + if (icons[i].infoWindowAnchor && + typeof icons[i].infoWindowAnchor === 'object' && + 'x' in icons[i].infoWindowAnchor && + 'y' in icons[i].infoWindowAnchor) { + icons[i].infoWindowAnchor = [icons[i].infoWindowAnchor.x, icons[i].infoWindowAnchor.anchor.y]; + } + if (icons[i].label && + common.isHTMLColorString(icons[i].label.color)) { + icons[i].label.color = common.HTMLColor2RGBA(icons[i].label.color); + } + } + + Object.defineProperty(self, 'icons', { + value: icons, + writable: false + }); + + self.addMarker = function (markerOptions, skipRedraw) { + + markerOptions = common.markerOptionsFilter(markerOptions); + var geocell = geomodel.getGeocell(markerOptions.position.lat, markerOptions.position.lng, self.MAX_RESOLUTION + 1); + + markerOptions._cluster = { + isRemoved: false, + isAdded: false, + geocell: geocell + }; + + if (typeof markerOptions.icon === 'string') { + if (markerOptions.icon.indexOf('://') === -1 && + markerOptions.icon.indexOf('.') === 0) { + + link = document.createElement('a'); + link.href = markerOptions.icon; + markerOptions.icon = link.protocol + '//' + link.host + link.pathname + link.search; + link = undefined; + } + } else if (typeof markerOptions.icon === 'object' && typeof markerOptions.icon.url === 'string') { + if (markerOptions.icon.url.indexOf('://') === -1 && + markerOptions.icon.url.indexOf('.') === 0) { + + link = document.createElement('a'); + link.href = markerOptions.icon.url; + markerOptions.icon.url = link.protocol + '//' + link.host + link.pathname + link.search; + link = undefined; + } + } + + + var marker = self._createMarker(markerOptions); + var markerId = marker.__pgmId.replace(/^.+-/, ''); + Object.defineProperty(marker, '__cid', { + value: markerId, + writable: false + }); + self._markerMap[markerId] = marker; + if (skipRedraw || !self._isReady) { + return marker; + } + self._triggerRedraw({ + force: true + }); + return marker; + }; + + self.addMarkers = function (markers) { + var results = []; + if (utils.isArray(markers) || Array.isArray(markers)) { + for (var i = 0; i < markers.length; i++) { + results.push(self.addMarker(markers[i], true)); + } + if (!self._isReady) { + return results; + } + self._triggerRedraw({ + force: true + }); + } + return results; + }; + + map.on(event.CAMERA_MOVE_END, self._onCameraMoved.bind(self)); + window.addEventListener('orientationchange', self._onCameraMoved.bind(self)); + + self.on('cluster_click', self.onClusterClicked); + self.on('nextTask', function () { + self._isWorking = false; + if (self._stopRequest || self._isRemoved || + self.taskQueue.length === 0 || !self._isReady) { + return; + } + self._triggerRedraw.call(self); + }); + + // self._triggerRedraw.call(self, { + // force: true + // }); + + if (self.debug) { + self.debugTimer = setInterval(function () { + console.log('self.taskQueue.push = ' + self.taskQueue.length); + }, 5000); + } + + return self; +}; + +utils.extend(MarkerCluster, Overlay); + +MarkerCluster.prototype.onClusterClicked = function (cluster) { + if (this._isRemoved) { + return null; + } + var self = this; + var polygon = self.get('polygon'); + var bounds = cluster.getBounds(); + if (self.boundsDraw) { + polygon.setPoints([ + bounds.southwest, + { + lat: bounds.northeast.lat, + lng: bounds.southwest.lng + }, + bounds.northeast, + { + lat: bounds.southwest.lat, + lng: bounds.northeast.lng + } + ]); + polygon.setVisible(true); + } + var zoomLevel = computeZoom(cluster.getBounds(), self.map.getDiv()); + zoomLevel += zoomLevel === self.map.get('camera_zoom') ? 1 : 0; + self.map.animateCamera({ + target: cluster.getBounds().getCenter(), + zoom: zoomLevel, + duration: 500 + }, function () { + if (self.boundsDraw) { + setTimeout(function () { + polygon.setVisible(false); + }, 500); + } + }); +}; + +Object.defineProperty(MarkerCluster.prototype, '_onCameraMoved', { + value: function () { + var self = this; + + if (self._isRemoved || self._stopRequest || !self._isReady) { + return null; + } + + self._triggerRedraw({ + force: false + }); + + }, + enumerable: false, + writable: false +}); + +MarkerCluster.prototype.remove = function (callback) { + var self = this; + self._stopRequest = self.hashCode; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + if (self.debug) { + clearInterval(self.debugTimer); + self.self.debugTimer = undefined; + } + + if (self._isWorking) { + setTimeout(arguments.callee.bind(self), 20); + return; + } + + var keys; + var resolution = self.get('resolution'), + activeMarker = self.map.get('active_marker'), + deleteClusters = []; + + self.taskQueue = []; + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + self.trigger('remove'); + + var activeMarkerId = activeMarker ? activeMarker.getId() : ''; + activeMarkerId = activeMarkerId.replace(/^.+\-/, ''); + var marker; + if (resolution === self.OUT_OF_RESOLUTION) { + while (self._clusters[resolution].length > 0) { + marker = self._clusters[resolution].shift(); + deleteClusters.push(marker.__cid); + if (marker.__cid === activeMarkerId) { + marker.trigger(event.INFO_CLOSE); + marker.hideInfoWindow(); + } + } + } else if (self._clusters[resolution]) { + keys = Object.keys(self._clusters[resolution]); + keys.forEach(function (geocell) { + var cluster = self._clusters[resolution][geocell]; + var noClusterMode = cluster.getMode() === cluster.NO_CLUSTER_MODE; + if (noClusterMode) { + cluster.getMarkers().forEach(function (marker) { + if (marker.__cid === activeMarkerId) { + marker.trigger(event.INFO_CLOSE); + marker.hideInfoWindow(); + } + deleteClusters.push(marker.__cid); + }); + } + if (!noClusterMode) { + deleteClusters.push(geocell); + } + cluster.remove(); + }); + } + + var resolver = function (resolve, reject) { + self.exec.call(self, + resolve.bind(self), + reject.bind(self), + self.getPluginName(), 'remove', [self.getId()], { + sync: true, + remove: true + }); + }; + + var answer; + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + answer = new Promise(resolver); + } + + keys = Object.keys(self._markerMap); + keys.forEach(function (markerId) { + try { + self._markerMap[markerId].remove(); + } catch(e) { + // ignore + } + }); + keys.forEach(function (markerId) { + self._markerMap[markerId].destroy(); + self._markerMap[markerId] = undefined; + delete self._markerMap[markerId]; + }); + if (self.boundsDraw && self.get('polygon')) { + self.get('polygon').remove(); + } + self.off(); + + return answer; +}; + + +Object.defineProperty(MarkerCluster.prototype, '_removeMarkerById', { + enumerable: false, + value: function (markerId) { + var self = this; + if (self._isRemoved) { + return null; + } + //if (markerId.indexOf(self.__cid + '-') === -1) { + //} + var marker = self._markerMap[markerId]; + if (!marker) { + return; + } + var isAdded = marker.get('_cluster').isAdded; + var resolutionList = Object.keys(self._clusters); + var resolution, geocellKey, cluster; + for (var i = 0; i < resolutionList.length; i++) { + resolution = parseInt(resolutionList[i], 10); + geocellKey = marker.get('_cluster').geocell.substr(0, resolution + 1); + if (geocellKey in self._clusters[resolution]) { + cluster = self._clusters[resolution][geocellKey]; + if (cluster) { + cluster.removeMarker(marker); + } + } + } + + marker.remove(); + marker.destroy(); + delete self._markerMap[markerId]; + if (isAdded) { + self.exec.call(self, null, null, self.getPluginName(), 'redrawClusters', [self.getId(), { + 'delete': [markerId] + }], { + sync: true + }); + } + }, + writable: false +}); + +MarkerCluster.prototype.getMarkerById = function (markerId) { + var self = this; + if (self._isRemoved) { + return null; + } + return self._markerMap[markerId]; +}; + +Object.defineProperty(MarkerCluster.prototype, '_getClusterByClusterId', { + enumerable: false, + value: function (clusterId) { + var self = this; + + if (self._isRemoved) { + return null; + } + var resolution = self.get('resolution'); + + if (!self._clusters[resolution]) { + return null; + } + + return self._clusters[resolution][clusterId]; + }, + writable: false +}); + + +Object.defineProperty(MarkerCluster.prototype, '_triggerRedraw', { + enumerable: false, + value: function (params) { + var self = this; + if (self._isRemoved || self._stopRequest || !self._isReady) { + return null; + } + + self.taskQueue.push(params); + if (self.debug) { + console.log('self.taskQueue.push = ' + self.taskQueue.length); + } + if (self._isRemoved || self._stopRequest || self.taskQueue.length > 1) { + return; + } + if (self.debug) { + self._clusterBounds.forEach(function(polyline, cb) { + polyline.remove(); + cb(); + }, function() { + self._clusterBounds.empty(); + var taskParams = self.taskQueue.pop(); + self.taskQueue.length = 0; + var visibleRegion = self.map.getVisibleRegion(); + self._redraw.call(self, { + visibleRegion: visibleRegion, + force: taskParams.force + }); + }); + } else { + var taskParams = self.taskQueue.pop(); + self.taskQueue.length = 0; + + var visibleRegion = self.map.getVisibleRegion(); + self._redraw.call(self, { + visibleRegion: visibleRegion, + force: taskParams.force + }); + } + }, + writable: false +}); + +Object.defineProperty(MarkerCluster.prototype, '_redraw', { + enumerable: false, + value: function (params) { + var self = this; + + if (self._isRemoved || self._stopRequest || self._isWorking || !self._isReady) { + return null; + } + self._isWorking = true; + var currentZoomLevel = self.map.getCameraZoom(), + prevResolution = self.get('resolution'); + + currentZoomLevel = currentZoomLevel < 0 ? 0 : currentZoomLevel; + self.set('zoom', currentZoomLevel); + + var resolution = 1; + resolution = self.maxZoomLevel > 3 && currentZoomLevel > 3 ? 2 : resolution; + resolution = self.maxZoomLevel > 5 && currentZoomLevel > 5 ? 3 : resolution; + resolution = self.maxZoomLevel > 7 && currentZoomLevel > 7 ? 4 : resolution; + resolution = self.maxZoomLevel > 9 && currentZoomLevel > 9 ? 5 : resolution; + resolution = self.maxZoomLevel > 11 && currentZoomLevel > 11 ? 6 : resolution; + resolution = self.maxZoomLevel > 13 && currentZoomLevel > 13 ? 7 : resolution; + resolution = self.maxZoomLevel > 15 && currentZoomLevel > 15 ? 8 : resolution; + resolution = self.maxZoomLevel > 17 && currentZoomLevel > 17 ? 9 : resolution; + resolution = self.maxZoomLevel > 19 && currentZoomLevel > 19 ? 10 : resolution; + resolution = self.maxZoomLevel > 21 && currentZoomLevel > 21 ? 11 : resolution; + + //------------------------------------------------------------------------ + // If the current viewport contains the previous viewport, + // and also the same resolution, + // skip this task except the params.force = true (such as addMarker) + //------------------------------------------------------------------------ + + var cellLen = resolution + 1; + var prevSWcell = self.get('prevSWcell'); + var prevNEcell = self.get('prevNEcell'); + + var distanceA = spherical.computeDistanceBetween(params.visibleRegion.farRight, params.visibleRegion.farLeft); + var distanceB = spherical.computeDistanceBetween(params.visibleRegion.farRight, params.visibleRegion.nearRight); + params.clusterDistance = Math.min(distanceA, distanceB) / 4; + var expandedRegion = params.visibleRegion; + + var swCell = geomodel.getGeocell(expandedRegion.southwest.lat, expandedRegion.southwest.lng, cellLen); + var neCell = geomodel.getGeocell(expandedRegion.northeast.lat, expandedRegion.northeast.lng, cellLen); + + if (!params.force && + prevSWcell === swCell && + prevNEcell === neCell) { + self.trigger('nextTask'); + return; + } + var nwCell = geomodel.getGeocell(expandedRegion.northeast.lat, expandedRegion.southwest.lng, cellLen); + var seCell = geomodel.getGeocell(expandedRegion.southwest.lat, expandedRegion.northeast.lng, cellLen); + + if (currentZoomLevel > self.maxZoomLevel || resolution === 0) { + resolution = self.OUT_OF_RESOLUTION; + } + self.set('resolution', resolution); + //console.log('--->prevResolution = ' + prevResolution + ', resolution = ' + resolution); + + var targetMarkers = []; + + if (self._isRemoved || self._stopRequest) { + self._isWorking = false; + return; + } + //---------------------------------------------------------------- + // Remove the clusters that is in outside of the visible region + //---------------------------------------------------------------- + if (resolution !== self.OUT_OF_RESOLUTION) { + self._clusters[resolution] = self._clusters[resolution] || {}; + } else { + self._clusters[resolution] = self._clusters[resolution] || []; + } + var deleteClusters = {}; + var keys, i, j; + var ignoreGeocells = []; + var allowGeocells = [swCell, neCell, nwCell, seCell]; + + var pos, cell; + var coners = [ + expandedRegion.northeast, + { + lat: expandedRegion.northeast.lat, + lng: expandedRegion.southwest.lng + }, + expandedRegion.southwest, + { + lat: expandedRegion.southwest.lat, + lng: expandedRegion.northeast.lng + }, + expandedRegion.northeast + ]; + for (j = 0; j < 4; j++) { + for (i = 0.25; i < 1; i += 0.25) { + pos = window.plugin.google.maps.geometry.spherical.interpolate(coners[j], coners[j + 1], i); + + cell = geomodel.getGeocell(pos.lat, pos.lng, cellLen); + if (allowGeocells.indexOf(cell) === -1) { + allowGeocells.push(cell); + } + } + } + + //console.log('---->548'); + var activeMarker = self.map.get('active_marker'); + var activeMarkerId = activeMarker ? activeMarker.getId() : ''; + activeMarkerId = activeMarkerId.replace(/^.+\-/, ''); + if (prevResolution === self.OUT_OF_RESOLUTION) { + if (resolution === self.OUT_OF_RESOLUTION) { + //-------------------------------------- + // Just camera move, no zoom changed + //-------------------------------------- + keys = Object.keys(self._markerMap); + keys.forEach(function (markerId) { + var marker = self._markerMap[markerId]; + if (self._isRemoved || + self._stopRequest || + !marker.isVisible() || + marker.get('_cluster').isRemoved || + marker.get('_cluster').isAdded) { + return; + } + + var geocell = marker.get('_cluster').geocell.substr(0, cellLen); + if (ignoreGeocells.indexOf(geocell) > -1) { + return; + } + + if (allowGeocells.indexOf(geocell) > -1) { + targetMarkers.push(marker); + return; + } + + if (expandedRegion.contains(marker.get('position'))) { + allowGeocells.push(geocell); + targetMarkers.push(marker); + } else { + ignoreGeocells.push(geocell); + } + }); + } else { + //-------------- + // zoom out + //-------------- + var marker; + var markerId; + while (self._clusters[self.OUT_OF_RESOLUTION].length > 0) { + marker = self._clusters[self.OUT_OF_RESOLUTION].shift(); + marker.get('_cluster').isAdded = false; + markerId = marker.__cid; + deleteClusters[markerId] = 1; + + if (marker.__cid === activeMarkerId) { + marker.trigger(event.INFO_CLOSE); + marker.hideInfoWindow(); + } + if (self.debug) { + console.log('---> (js:489)delete:' + markerId); + } + } + keys = Object.keys(self._markerMap); + keys.forEach(function (markerId) { + var marker = self._markerMap[markerId]; + if (self._isRemoved || + !marker.isVisible() || + self._stopRequest || + marker.get('_cluster').isRemoved) { + return; + } + + var geocell = marker.get('_cluster').geocell.substr(0, cellLen); + if (ignoreGeocells.indexOf(geocell) > -1) { + return; + } + + if (allowGeocells.indexOf(geocell) > -1) { + targetMarkers.push(marker); + return; + } + + if (expandedRegion.contains(marker.get('position'))) { + allowGeocells.push(geocell); + targetMarkers.push(marker); + } else { + ignoreGeocells.push(geocell); + } + }); + } + + } else if (resolution === prevResolution) { + //console.log('--->prevResolution(' + prevResolution + ') == resolution(' + resolution + ')'); + //-------------------------------------- + // Just camera move, no zoom changed + //-------------------------------------- + + keys = Object.keys(self._clusters[prevResolution]); + keys.forEach(function (geocell) { + if (self._isRemoved || self._stopRequest) { + return; + } + var cluster = self._clusters[prevResolution][geocell]; + var bounds = cluster.getBounds(); + + if (!self._isRemoved && + !expandedRegion.contains(bounds.northeast) && + !expandedRegion.contains(bounds.southwest)) { + ignoreGeocells.push(geocell); + + if (cluster.getMode() === cluster.NO_CLUSTER_MODE) { + cluster.getMarkers().forEach(function (marker) { + var markerId = marker.__cid; + deleteClusters[markerId] = 1; + marker.get('_cluster').isAdded = false; + if (self.debug) { + console.log('---> (js:534)delete:' + markerId); + } + if (marker.__cid === activeMarkerId) { + marker.trigger(event.INFO_CLOSE); + marker.hideInfoWindow(); + } + }); + } else { + deleteClusters[geocell] = 1; + if (self.debug) { + console.log('---> (js:549)delete:' + geocell); + } + } + cluster.remove(); + delete self._clusters[resolution][geocell]; + } + }); + keys = Object.keys(self._markerMap); + keys.forEach(function (markerId) { + var marker = self._markerMap[markerId]; + var geocell = marker.get('_cluster').geocell.substr(0, cellLen); + if (self._isRemoved || + self._stopRequest || + marker.get('_cluster').isRemoved || + !marker.isVisible() || + ignoreGeocells.indexOf(geocell) > -1 || + marker.get('_cluster').isAdded) { + return; + } + + if (allowGeocells.indexOf(geocell) > -1) { + targetMarkers.push(marker); + return; + } + + if (expandedRegion.contains(marker.get('position'))) { + targetMarkers.push(marker); + allowGeocells.push(geocell); + } else { + ignoreGeocells.push(geocell); + marker.get('_cluster').isAdded = false; + } + }); + + } else if (prevResolution in self._clusters) { + //console.log('--->prevResolution(' + prevResolution + ') != resolution(' + resolution + ')'); + + if (prevResolution < resolution) { + //-------------- + // zooming in + //-------------- + keys = Object.keys(self._clusters[prevResolution]); + keys.forEach(function (geocell) { + if (self._isRemoved) { + return; + } + var cluster = self._clusters[prevResolution][geocell]; + var noClusterMode = cluster.getMode() === cluster.NO_CLUSTER_MODE; + cluster.getMarkers().forEach(function (marker) { + if (self._isRemoved || + self._stopRequest) { + return; + } + var markerId = marker.__cid; + marker.get('_cluster').isAdded = false; + //targetMarkers.push(markerOpts); + if (noClusterMode) { + if (self.debug) { + console.log('---> (js:581)delete:' + markerId); + } + if (marker.__cid === activeMarkerId) { + marker.trigger(event.INFO_CLOSE); + marker.hideInfoWindow(); + } + deleteClusters[markerId] = 1; + } + }); + if (!noClusterMode) { + if (self.debug) { + console.log('---> (js:597)delete:' + geocell); + } + deleteClusters[geocell] = 1; + } + cluster.remove(); + }); + } else { + //-------------- + // zooming out + //-------------- + keys = Object.keys(self._clusters[prevResolution]); + keys.forEach(function (geocell) { + if (self._stopRequest || + self._isRemoved) { + return; + } + var cluster = self._clusters[prevResolution][geocell]; + var noClusterMode = cluster.getMode() === cluster.NO_CLUSTER_MODE; + cluster.getMarkers().forEach(function (marker) { + marker.get('_cluster').isAdded = false; + var markerId = marker.__cid; + if (noClusterMode) { + if (self.debug) { + console.log('---> (js:614)delete:' + markerId); + } + if (marker.__cid === activeMarkerId) { + marker.trigger(event.INFO_CLOSE); + marker.hideInfoWindow(); + } + deleteClusters[markerId] = 1; + } + }); + if (!noClusterMode) { + deleteClusters[geocell] = 1; + if (self.debug) { + console.log('---> (js:632)delete:' + geocell); + } + } + cluster.remove(); + + geocell = geocell.substr(0, cellLen); + }); + } + keys = Object.keys(self._markerMap); + keys.forEach(function (markerId) { + if (self._stopRequest || + self._isRemoved) { + return; + } + var marker = self._markerMap[markerId]; + var geocell = marker.get('_cluster').geocell.substr(0, cellLen); + if (marker.get('_cluster').isRemoved || + ignoreGeocells.indexOf(geocell) > -1) { + marker.get('_cluster').isAdded = false; + return; + } + if (marker.get('_cluster').isAdded) { + return; + } + + if (allowGeocells.indexOf(geocell) > -1) { + targetMarkers.push(marker); + return; + } + + if (expandedRegion.contains(marker.get('position'))) { + targetMarkers.push(marker); + allowGeocells.push(geocell); + } else { + ignoreGeocells.push(geocell); + marker.get('_cluster').isAdded = false; + } + }); + delete self._clusters[prevResolution]; + } else { + //console.log('-----> initialize'); + keys = Object.keys(self._markerMap); + keys.forEach(function (markerId) { + if (self._stopRequest || + self._isRemoved) { + return; + } + var marker = self._markerMap[markerId]; + var geocell = marker.get('_cluster').geocell.substr(0, cellLen); + if (marker.get('_cluster').isRemoved || + ignoreGeocells.indexOf(geocell) > -1) { + return; + } + + if (allowGeocells.indexOf(geocell) > -1) { + targetMarkers.push(marker); + return; + } + if (expandedRegion.contains(marker.get('position'))) { + targetMarkers.push(marker); + allowGeocells.push(geocell); + } else { + ignoreGeocells.push(geocell); + } + }); + } + + if (self._stopRequest || + self._isRemoved) { + self._isWorking = false; + return; + } + if (self.debug) { + console.log('targetMarkers = ' + targetMarkers.length); + } + + //-------------------------------- + // Pick up markers are containted in the current viewport. + //-------------------------------- + var new_or_update_clusters = []; + + if (params.force || + resolution == self.OUT_OF_RESOLUTION || + resolution !== prevResolution || + prevSWcell !== swCell || + prevNEcell !== neCell) { + + self.set('prevSWcell', swCell); + self.set('prevNEcell', neCell); + + if (resolution !== self.OUT_OF_RESOLUTION) { + + //------------------ + // Create clusters + //------------------ + var prepareClusters = {}; + targetMarkers.forEach(function (marker) { + if (marker.get('_cluster').isAdded) { + if (self.debug) { + console.log('isAdded', marker); + } + return; + } + var geocell = marker.get('_cluster').geocell.substr(0, resolution + 1); + prepareClusters[geocell] = prepareClusters[geocell] || []; + prepareClusters[geocell].push(marker); + + if (marker && marker.__cid === activeMarkerId) { + marker.trigger(event.INFO_CLOSE); + marker.hideInfoWindow(); + } + }); + + if (self.debug) { + console.log('prepareClusters = ', prepareClusters, targetMarkers); + } + + //------------------------------------------ + // Create/update clusters + //------------------------------------------ + keys = Object.keys(prepareClusters); + + var sortedClusters = []; + keys.forEach(function (geocell) { + var cluster = self.getClusterByGeocellAndResolution(geocell, resolution); + cluster.addMarkers(prepareClusters[geocell]); + + cluster._markerCenter = cluster.getBounds().getCenter(); + //cluster._distanceFrom0 = spherical.computeDistanceBetween({lat: 0, lng: 0}, cluster._markerCenter); + sortedClusters.push(cluster); + }); + + sortedClusters = sortedClusters.sort(function (a, b) { + return a.geocell.localeCompare(b.geocell); + }); + + //------------------------- + // Union close clusters + //------------------------- + var cluster, anotherCluster, distance; + var unionedMarkers = []; + i = 0; + while (i < sortedClusters.length) { + cluster = sortedClusters[i]; + for (j = i + 1; j < sortedClusters.length; j++) { + anotherCluster = sortedClusters[j]; + distance = spherical.computeDistanceBetween(cluster._markerCenter, anotherCluster._markerCenter); + if (distance < params.clusterDistance) { + if (self.debug) { + console.log('---> (js:763)delete:' + anotherCluster.geocell); + } + cluster.addMarkers(anotherCluster.getMarkers()); + deleteClusters[anotherCluster.getId()] = 1; + delete self._clusters[resolution][anotherCluster.geocell]; + self._clusters[resolution][cluster.geocell] = cluster; + i = j; + } else { + break; + } + } + i++; + cluster._markerCnt = cluster.getItemLength(); + unionedMarkers.push(cluster); + } + + unionedMarkers.forEach(function (cluster) { + + var icon = self.getClusterIcon(cluster), + clusterOpts = { + 'count': cluster.getItemLength(), + 'position': cluster.getBounds().getCenter(), + '__pgmId': cluster.getId() + }; + + if (self.debug) { + clusterOpts.geocell = cluster.geocell; + } + + if (icon) { + clusterOpts.icon = icon; + clusterOpts.isClusterIcon = true; + if (cluster.getMode() === cluster.NO_CLUSTER_MODE) { + cluster.getMarkers().forEach(function (marker) { + var markerId = marker.__cid; + deleteClusters[markerId] = 1; + if (self.debug) { + console.log('---> (js:800)delete:' + markerId); + } + }); + } + if (self.debug) { + console.log('---> (js:805)add:' + clusterOpts.__pgmId, icon); + var geocell = clusterOpts.geocell.substr(0, cellLen); + var bounds = self._geocellBounds[geocell] || geomodel.computeBox(geocell); + self._geocellBounds[geocell] = bounds; + self.map.addPolyline({ + color: 'blue', + points: [ + bounds.southwest, + { + lat: bounds.southwest.lat, + lng: bounds.northeast.lng + }, + bounds.northeast, + { + lat: bounds.northeast.lat, + lng: bounds.southwest.lng + }, + bounds.southwest + ] + }, function (polyline) { + self._clusterBounds.push(polyline); + }); + } + cluster.setMode(cluster.CLUSTER_MODE); + new_or_update_clusters.push(clusterOpts); + return; + } + + cluster.getMarkers().forEach(function (marker) { + if (!marker.get('_cluster').isAdded) { + return; + } + var markerId = marker.__cid; + delete deleteClusters[markerId]; + marker.get('_cluster').isClusterIcon = false; + if (self.debug) { + console.log('---> (js:831)add:' + markerId + ', isAdded = ' + marker.get('_cluster').isAdded); + marker.set('title', markerId, true); + } + + var opts = marker.getOptions(); + opts.__pgmId = markerId; + new_or_update_clusters.push(opts); + }); + cluster.setMode(cluster.NO_CLUSTER_MODE); + }); + } else { + cellLen = swCell.length; + targetMarkers.forEach(function (marker) { + if (marker.get('_cluster').isAdded) { + return; + } + var markerId = marker.__cid; + marker.get('_cluster').isClusterIcon = false; + if (self.debug) { + console.log('---> (js:859)add:' + markerId); + marker.set('title', markerId, true); + } + delete deleteClusters[markerId]; + marker.get('_cluster').isAdded = true; + var opts = marker.getOptions(); + opts.__pgmId = marker.__cid; + new_or_update_clusters.push(opts); + self._clusters[self.OUT_OF_RESOLUTION].push(marker); + }); + } + } + var delete_clusters = Object.keys(deleteClusters); + + if (self._stopRequest || + new_or_update_clusters.length === 0 && delete_clusters.length === 0) { + self.trigger('nextTask'); + return; + } + + if (self._stopRequest || + self._isRemoved) { + self._isWorking = false; + return; + } + + self.exec.call(self, function (allResults) { + var markerIDs = Object.keys(allResults); + markerIDs.forEach(function (markerId) { + if (!self._markerMap[markerId]) { + return; + } + var marker = self._markerMap[markerId]; + var size = allResults[markerId]; + if (typeof marker.get('icon') === 'string') { + marker.set('icon', { + 'url': marker.get('icon'), + 'size': size, + 'anchor': [size.width / 2, size.height] + }, true); + } else { + var icon = marker.get('icon') || {}; + icon.size = icon.size || size; + icon.anchor = icon.anchor || [size.width / 2, size.height]; + self._markerMap[markerId].set('icon', icon, true); + } + marker.set('infoWindowAnchor', marker.get('infoWindowAnchor') || [marker.get('icon').size.width / 2, 0], true); + }); + self.trigger('nextTask'); + }, self.errorHandler, self.getPluginName(), 'redrawClusters', [self.getId(), { + 'resolution': resolution, + 'new_or_update': new_or_update_clusters, + 'delete': delete_clusters + }], { + sync: true + }); + // console.log({ + // 'resolution': resolution, + // 'new_or_update': new_or_update_clusters, + // 'delete': delete_clusters + // }); + } +}); + +MarkerCluster.prototype.getClusterIcon = function (cluster) { + var self = this, + hit, + clusterCnt = cluster.getItemLength(); + + for (var i = 0; i < self.icons.length; i++) { + hit = false; + if ('min' in self.icons[i]) { + if (clusterCnt >= self.icons[i].min) { + if ('max' in self.icons[i]) { + hit = (clusterCnt <= self.icons[i].max); + } else { + hit = true; + } + } + } else { + if ('max' in self.icons[i]) { + hit = (clusterCnt <= self.icons[i].max); + } + } + if (hit) { + return self.icons[i]; + } + } + return null; +}; + +MarkerCluster.prototype._createMarker = function (markerOpts) { + var self = this; + var markerId = self.getId() + '-marker_' + Math.floor(Date.now() * Math.random()); + var marker = new Marker(self.getMap(), markerOpts, exec, { + className: 'MarkerCluster', + __pgmId: markerId + }); + marker._privateInitialize(markerOpts); + delete marker._privateInitialize; + + // Recalulate geocell if marker position is changed. + marker.onThrottled('position_changed', function(ignore, newPosition) { + marker.get('_cluster').geocell = geomodel.getGeocell(newPosition.lat, newPosition.lng, self.MAX_RESOLUTION + 1); + }, 500); + marker.one(marker.getId() + '_remove', function () { + self._removeMarkerById(markerId); + }); + return marker; +}; + +MarkerCluster.prototype.getClusterByGeocellAndResolution = function (geocell, resolution) { + var self = this; + geocell = geocell.substr(0, resolution + 1); + + var cluster = self._clusters[resolution][geocell]; + if (!cluster) { + cluster = new Cluster(geocell, geocell); + self._clusters[resolution][geocell] = cluster; + } + return cluster; +}; + +/* + * http://jsfiddle.net/john_s/BHHs8/6/ + */ +function latRad(lat) { + var sin = Math.sin(lat * Math.PI / 180); + var radX2 = Math.log((1 + sin) / (1 - sin)) / 2; + return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2; +} + +function zoom(mapPx, worldPx, fraction) { + return Math.log(mapPx / worldPx / fraction) / Math.LN2; +} + +function computeZoom(bounds, mapDim) { + var tileSize = cordova.platformId === 'browser' ? 256 : 512; + var ZOOM_MAX = 21; + + var ne = bounds.northeast; + var sw = bounds.southwest; + + var latFraction = (latRad(ne.lat) - latRad(sw.lat)) / Math.PI; + + var lngDiff = ne.lng - sw.lng; + var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360; + + var latZoom = zoom(mapDim.offsetHeight, tileSize, latFraction); + var lngZoom = zoom(mapDim.offsetWidth, tileSize, lngFraction); + + return Math.min(latZoom, lngZoom, ZOOM_MAX); +} + +module.exports = MarkerCluster; diff --git a/node_modules/cordova-plugin-googlemaps/www/Overlay.js b/node_modules/cordova-plugin-googlemaps/www/Overlay.js new file mode 100644 index 0000000..d5adf84 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Overlay.js @@ -0,0 +1,120 @@ + +var BaseClass = require('./BaseClass'), + utils = require('cordova/utils'), + BaseArrayClass = require('./BaseArrayClass'); + +/***************************************************************************** + * Overlay Class + *****************************************************************************/ +var Overlay = function (map, options, className, _exec, extras) { + extras = extras || {}; + BaseClass.apply(this); + + var self = this; + + //----------------------------------------------- + // Sets the initialize option to each property + //----------------------------------------------- + var ignores = ['map', '__pgmId', 'hashCode', 'type']; + if (extras.ignores) { + ignores = ignores.concat(extras.ignores); + } + for (var key in options) { + if (ignores.indexOf(key) === -1) { + self.set(key, options[key]); + } + } + + //------------------------------------------------------------------------------- + // If app code wants to execute some method before `_isReady = true`, + // just stack in to the internal queue. + // If this overlay is ready, execute it. + //------------------------------------------------------------------------------- + var cmdQueue = new BaseArrayClass(); + cmdQueue.on('insert_at', function () { + if (!self._isReady) { + return; + } + var cmd; + while (cmdQueue.getLength() > 0) { + cmd = cmdQueue.removeAt(0, true); + if (cmd && cmd.target && cmd.args && cmd.args[0] !== 'nop') { + _exec.apply(cmd.target, cmd.args); + } + } + }); + + Object.defineProperty(self, '_cmdQueue', { + enumerable: false, + value: cmdQueue, + writable: false + }); + + Object.defineProperty(self, '_isReady', { + value: false, + writable: true + }); + Object.defineProperty(self, 'map', { + value: map, + writable: false + }); + Object.defineProperty(self, '__pgmId', { + value: extras.__pgmId || (className.toLowerCase()) + '_' + this.hashCode, + writable: false + }); + Object.defineProperty(self, 'type', { + value: className, + writable: false + }); + + Object.defineProperty(self, 'getPluginName', { + writable: false, + value: function () { + return this.map.getId() + '-' + className.toLowerCase(); + } + }); +}; + +utils.extend(Overlay, BaseClass); + +Overlay.prototype._privateInitialize = function (options) { + var self = this; + //----------------------------------------------- + // Sets the initialize option to each property + //----------------------------------------------- + if (options) { + var ignores = ['map', '__pgmId', 'hashCode', 'type']; + for (var key in options) { + if (ignores.indexOf(key) === -1) { + self.set(key, options[key], true); + } + } + } + + //----------------------------------------------- + // Trigger internal command queue + //----------------------------------------------- + Object.defineProperty(self, '_isReady', { + value: true, + writable: false + }); + self.exec('nop'); +}; + +Overlay.prototype.exec = function () { + this._cmdQueue.push.call(this._cmdQueue, { + target: this, + args: Array.prototype.slice.call(arguments, 0) + }); +}; +Overlay.prototype.getId = function () { + return this.__pgmId; +}; +Overlay.prototype.getMap = function () { + return this.map; +}; +Overlay.prototype.getHashCode = function () { + return this.hashCode; +}; + +module.exports = Overlay; diff --git a/node_modules/cordova-plugin-googlemaps/www/Polygon.js b/node_modules/cordova-plugin-googlemaps/www/Polygon.js new file mode 100644 index 0000000..277a343 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Polygon.js @@ -0,0 +1,248 @@ +var utils = require('cordova/utils'), + common = require('./Common'), + Overlay = require('./Overlay'); + +/***************************************************************************** + * Polygon Class + *****************************************************************************/ +var Polygon = function (map, polygonOptions, _exec) { + Overlay.call(this, map, polygonOptions, 'Polygon', _exec); + + var self = this; + var polygonId = this.getId(); + + //-------------------------- + // points property + //-------------------------- + var pointsProperty = common.createMvcArray(polygonOptions.points); + pointsProperty.on('set_at', function (index) { + var value = common.getLatLng(pointsProperty.getAt(index)); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setPointAt', [polygonId, index, value]); + }); + pointsProperty.on('insert_at', function (index) { + var value = common.getLatLng(pointsProperty.getAt(index)); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'insertPointAt', [polygonId, index, value]); + }); + pointsProperty.on('remove_at', function (index) { + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'removePointAt', [polygonId, index]); + }); + + Object.defineProperty(self, 'points', { + value: pointsProperty, + writable: false + }); + //-------------------------- + // holes property + //-------------------------- + var holesProperty = common.createMvcArray(polygonOptions.holes); + var _holes = common.createMvcArray(holesProperty.getArray()); + + holesProperty.on('set_at', function (index) { + var value = common.getLatLng(holesProperty.getAt(index)); + _holes.setAt(index, value); + }); + holesProperty.on('remove_at', function (index) { + _holes.removeAt(index); + }); + holesProperty.on('insert_at', function (index) { + var array = holesProperty.getAt(index); + if (array && (array instanceof Array || Array.isArray(array))) { + array = common.createMvcArray(array); + } + array.on('insert_at', function (idx) { + var value = common.getLatLng(array.getAt(idx)); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'insertPointOfHoleAt', [polygonId, index, idx, value]); + }); + array.on('set_at', function (idx) { + var value = common.getLatLng(array.getAt(idx)); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setPointOfHoleAt', [polygonId, index, idx, value]); + }); + array.on('remove_at', function (idx) { + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'removePointOfHoleAt', [polygonId, index, idx]); + }); + + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'insertHoleAt', [polygonId, index, array.getArray()]); + }); + + Object.defineProperty(self, 'holes', { + value: holesProperty, + writable: false + }); + + //-------------------------- + // other properties + //--------------------------. + // var ignores = ['map', '__pgmId', 'hashCode', 'type', 'points', 'holes']; + // for (var key in polygonOptions) { + // if (ignores.indexOf(key) === -1) { + // self.set(key, polygonOptions[key]); + // } + // } + //----------------------------------------------- + // Sets event listeners + //----------------------------------------------- + self.on('clickable_changed', function () { + var clickable = self.get('clickable'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setClickable', [self.getId(), clickable]); + }); + self.on('geodesic_changed', function () { + var geodesic = self.get('geodesic'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setGeodesic', [self.getId(), geodesic]); + }); + self.on('zIndex_changed', function () { + var zIndex = self.get('zIndex'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setZIndex', [self.getId(), zIndex]); + }); + self.on('visible_changed', function () { + var visible = self.get('visible'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setVisible', [self.getId(), visible]); + }); + self.on('strokeWidth_changed', function () { + var width = self.get('strokeWidth'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setStrokeWidth', [self.getId(), width]); + }); + self.on('strokeColor_changed', function () { + var color = self.get('strokeColor'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setStrokeColor', [self.getId(), common.HTMLColor2RGBA(color, 0.75)]); + }); + self.on('fillColor_changed', function () { + var color = self.get('fillColor'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setFillColor', [self.getId(), common.HTMLColor2RGBA(color, 0.75)]); + }); + +}; + +utils.extend(Polygon, Overlay); + +Polygon.prototype.remove = function (callback) { + var self = this; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + self.trigger(this.__pgmId + '_remove'); + + var resolver = function(resolve, reject) { + self.exec.call(self, + function() { + self.destroy(); + resolve.call(self); + }, + reject.bind(self), + self.getPluginName(), 'remove', [self.getId()], { + remove: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + +}; + +Polygon.prototype.setPoints = function (points) { + var self = this; + var mvcArray = self.points; + mvcArray.empty(true); + + var i; + + for (i = 0; i < points.length; i++) { + mvcArray.push(common.getLatLng(points[i]), true); + } + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setPoints', [self.__pgmId, mvcArray.getArray()]); + return self; +}; +Polygon.prototype.getPoints = function () { + return this.points; +}; +Polygon.prototype.setHoles = function (holes) { + var self = this; + var mvcArray = this.holes; + mvcArray.empty(true); + + holes = holes || []; + if (holes.length > 0 && !utils.isArray(holes[0])) { + holes = [holes]; + } + holes.forEach(function (hole) { + if (!utils.isArray(hole)) { + hole = [hole]; + mvcArray.push(hole, true); + } else { + var newHole = []; + for (var i = 0; i < hole.length; i++) { + newHole.push(common.getLatLng(hole[i])); + } + mvcArray.push(newHole, true); + } + }); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setHoles', [self.__pgmId, mvcArray.getArray()]); + return this; +}; +Polygon.prototype.getHoles = function () { + return this.holes; +}; +Polygon.prototype.setFillColor = function (color) { + this.set('fillColor', color); + return this; +}; +Polygon.prototype.getFillColor = function () { + return this.get('fillColor'); +}; +Polygon.prototype.setStrokeColor = function (color) { + this.set('strokeColor', color); + return this; +}; +Polygon.prototype.getStrokeColor = function () { + return this.get('strokeColor'); +}; +Polygon.prototype.setStrokeWidth = function (width) { + this.set('strokeWidth', width); + return this; +}; +Polygon.prototype.getStrokeWidth = function () { + return this.get('strokeWidth'); +}; +Polygon.prototype.setVisible = function (visible) { + visible = common.parseBoolean(visible); + this.set('visible', visible); + return this; +}; +Polygon.prototype.getVisible = function () { + return this.get('visible'); +}; +Polygon.prototype.setClickable = function (clickable) { + clickable = common.parseBoolean(clickable); + this.set('clickable', clickable); + return this; +}; +Polygon.prototype.getClickable = function () { + return this.get('clickable'); +}; +Polygon.prototype.setGeodesic = function (geodesic) { + geodesic = common.parseBoolean(geodesic); + this.set('geodesic', geodesic); + return this; +}; +Polygon.prototype.getGeodesic = function () { + return this.get('geodesic'); +}; +Polygon.prototype.setZIndex = function (zIndex) { + this.set('zIndex', zIndex); + return this; +}; +Polygon.prototype.getZIndex = function () { + return this.get('zIndex'); +}; + +module.exports = Polygon; diff --git a/node_modules/cordova-plugin-googlemaps/www/Polyline.js b/node_modules/cordova-plugin-googlemaps/www/Polyline.js new file mode 100644 index 0000000..090fd17 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Polyline.js @@ -0,0 +1,189 @@ +var utils = require('cordova/utils'), + common = require('./Common'), + Overlay = require('./Overlay'); + +/***************************************************************************** + * Polyline Class + *****************************************************************************/ +var Polyline = function (map, polylineOptions, _exec) { + Overlay.call(this, map, polylineOptions, 'Polyline', _exec, { + 'ignores': ['points'] + }); + + var self = this; + var polylineId = this.getId(); + + var pointsProperty = common.createMvcArray(polylineOptions.points); + pointsProperty.on('set_at', function (index) { + if (self._isRemoved) return; + var value = common.getLatLng(pointsProperty.getAt(index)); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setPointAt', [polylineId, index, value]); + }); + pointsProperty.on('insert_at', function (index) { + if (self._isRemoved) return; + var value = common.getLatLng(pointsProperty.getAt(index)); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'insertPointAt', [polylineId, index, value]); + }); + pointsProperty.on('remove_at', function (index) { + if (self._isRemoved) return; + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'removePointAt', [polylineId, index]); + }); + + Object.defineProperty(self, 'points', { + value: pointsProperty, + writable: false + }); + //----------------------------------------------- + // Sets the initialize option to each property + //----------------------------------------------- + // var ignores = ['map', '__pgmId', 'hashCode', 'type', 'points']; + // for (var key in polylineOptions) { + // if (ignores.indexOf(key) === -1) { + // self.set(key, polylineOptions[key]); + // } + // } + + //----------------------------------------------- + // Sets event listeners + //----------------------------------------------- + self.on('geodesic_changed', function () { + if (self._isRemoved) return; + var geodesic = self.get('geodesic'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setGeodesic', [self.getId(), geodesic]); + }); + self.on('zIndex_changed', function () { + if (self._isRemoved) return; + var zIndex = self.get('zIndex'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setZIndex', [self.getId(), zIndex]); + }); + self.on('clickable_changed', function () { + if (self._isRemoved) return; + var clickable = self.get('clickable'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setClickable', [self.getId(), clickable]); + }); + self.on('visible_changed', function () { + if (self._isRemoved) return; + var visible = self.get('visible'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setVisible', [self.getId(), visible]); + }); + self.on('strokeWidth_changed', function () { + if (self._isRemoved) return; + var strokeWidth = self.get('strokeWidth'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setStrokeWidth', [self.getId(), strokeWidth]); + }); + self.on('strokeColor_changed', function () { + if (self._isRemoved) return; + var color = self.get('strokeColor'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setStrokeColor', [self.getId(), common.HTMLColor2RGBA(color, 0.75)]); + }); + +}; + +utils.extend(Polyline, Overlay); + +Polyline.prototype.setPoints = function (points) { + var self = this; + var mvcArray = self.points; + mvcArray.empty(true); + + var i; + + for (i = 0; i < points.length; i++) { + mvcArray.push({ + 'lat': points[i].lat, + 'lng': points[i].lng + }, true); + } + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setPoints', [self.__pgmId, mvcArray.getArray()]); + return self; +}; +Polyline.prototype.getPoints = function () { + return this.points; +}; +Polyline.prototype.setStrokeColor = function (color) { + this.set('strokeColor', color); + return this; +}; +Polyline.prototype.getStrokeColor = function () { + return this.get('strokeColor'); +}; +Polyline.prototype.setStrokeWidth = function (width) { + this.set('strokeWidth', width); + return this; +}; +Polyline.prototype.getStrokeWidth = function () { + return this.get('strokeWidth'); +}; +Polyline.prototype.setVisible = function (visible) { + visible = common.parseBoolean(visible); + this.set('visible', visible); + return this; +}; +Polyline.prototype.getVisible = function () { + return this.get('visible'); +}; +Polyline.prototype.setClickable = function (clickable) { + clickable = common.parseBoolean(clickable); + this.set('clickable', clickable); + return this; +}; +Polyline.prototype.getClickable = function () { + return this.get('clickable'); +}; +Polyline.prototype.setGeodesic = function (geodesic) { + geodesic = common.parseBoolean(geodesic); + this.set('geodesic', geodesic); + return this; +}; +Polyline.prototype.getGeodesic = function () { + return this.get('geodesic'); +}; +Polyline.prototype.setZIndex = function (zIndex) { + this.set('zIndex', zIndex); + return this; +}; +Polyline.prototype.getZIndex = function () { + return this.get('zIndex'); +}; + +Polyline.prototype.remove = function (callback) { + var self = this; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + + var resolver = function (resolve, reject) { + self.exec.call(self, + function () { + self.destroy(); + resolve.call(self); + }, + reject.bind(self), + self.getPluginName(), 'remove', [self.getId()], { + remove: true + }); + }; + + var result; + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + result = new Promise(resolver); + } + + if (self.points) { + self.points.empty(); + } + self.trigger(self.__pgmId + '_remove'); + + return result; +}; +module.exports = Polyline; diff --git a/node_modules/cordova-plugin-googlemaps/www/Promise.js b/node_modules/cordova-plugin-googlemaps/www/Promise.js new file mode 100644 index 0000000..5187e59 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Promise.js @@ -0,0 +1,20 @@ +// Copyright (c) 2013 Forbes Lindesay +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +!function n(t,e,r){function o(u,f){if(!e[u]){if(!t[u]){var c="function"==typeof require&&require;if(!f&&c)return c(u,!0);if(i)return i(u,!0);var s=new Error("Cannot find module '"+u+"'");throw s.code="MODULE_NOT_FOUND",s}var l=e[u]={exports:{}};t[u][0].call(l.exports,function(n){var e=t[u][1][n];return o(e?e:n)},l,l.exports,n,t,e,r)}return e[u].exports}for(var i="function"==typeof require&&require,u=0;ul){for(var t=0,e=f.length-s;e>t;t++)f[t]=f[t+s];f.length-=s,s=0}}f.length=0,s=0,c=!1}function o(n){var t=1,e=new a(n),r=document.createTextNode("");return e.observe(r,{characterData:!0}),function(){t=-t,r.data=t}}function i(n){return function(){function t(){clearTimeout(e),clearInterval(r),n()}var e=setTimeout(t,0),r=setInterval(t,50)}}t.exports=e;var u,f=[],c=!1,s=0,l=1024,a=n.MutationObserver||n.WebKitMutationObserver;u="function"==typeof a?o(r):i(r),e.requestFlush=u,e.makeRequestCallFromTimer=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],5:[function(n,t,e){"function"!=typeof Promise.prototype.done&&(Promise.prototype.done=function(n,t){var e=arguments.length?this.then.apply(this,arguments):this;e.then(null,function(n){setTimeout(function(){throw n},0)})})},{}],6:[function(n,t,e){n("asap");"undefined"==typeof Promise&&(Promise=n("./lib/core.js"),n("./lib/es6-extensions.js")),n("./polyfill-done.js")},{"./lib/core.js":1,"./lib/es6-extensions.js":2,"./polyfill-done.js":5,asap:3}]},{},[6]); diff --git a/node_modules/cordova-plugin-googlemaps/www/StreetViewPanorama.js b/node_modules/cordova-plugin-googlemaps/www/StreetViewPanorama.js new file mode 100644 index 0000000..ac3eaa5 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/StreetViewPanorama.js @@ -0,0 +1,320 @@ +var utils = require('cordova/utils'), + common = require('./Common'), + event = require('./event'), + Overlay = require('./Overlay'); + +/***************************************************************************** + * StreetViewPanorama Class + *****************************************************************************/ +var exec; +var StreetViewPanorama = function(streetViewId, _exec) { + exec = _exec; + Overlay.call(this, this, {}, 'StreetViewPanorama', _exec, { + __pgmId: streetViewId + }); + + var self = this; + self.set('visible', true); + self.set('gesture_panning', true); + self.set('gesture_zoom', true); + self.set('control_navigation', true); + self.set('control_streetNames', true); + self.set('camera_zoom', 0); + self.set('camera_tilt', 0); + self.set('camera_bearing', 0); + + //----------------------------------------------- + // Sets event listeners + //----------------------------------------------- + self.on('gesture_panning_changed', function() { + var booleanValue = self.get('gesture_panning'); + self.exec.call(self, null, self.errorHandler, self.__pgmId, 'setPanningGesturesEnabled', [booleanValue]); + }); + self.on('gesture_zoom_changed', function() { + var booleanValue = self.get('gesture_zoom'); + self.exec.call(self, null, self.errorHandler, self.__pgmId, 'setZoomGesturesEnabled', [booleanValue]); + }); + self.on('control_navigation_changed', function() { + var booleanValue = self.get('control_navigation'); + self.exec.call(self, null, self.errorHandler, self.__pgmId, 'setNavigationEnabled', [booleanValue]); + }); + self.on('control_streetNames_changed', function() { + var booleanValue = self.get('control_streetNames'); + self.exec.call(self, null, self.errorHandler, self.__pgmId, 'setStreetNamesEnabled', [booleanValue]); + }); + self.on('visible_changed', function() { + var booleanValue = self.get('visible'); + self.exec.call(self, null, self.errorHandler, self.__pgmId, 'setVisible', [booleanValue]); + }); +}; + + +utils.extend(StreetViewPanorama, Overlay); + +StreetViewPanorama.prototype.getPluginName = function() { + return this.map.getId() + '-streetview'; +}; + +StreetViewPanorama.prototype.getHashCode = function() { + return this.hashCode; +}; + +StreetViewPanorama.prototype.getId = function() { + return this.__pgmId; +}; +StreetViewPanorama.prototype.getPanorama = function(meta, panorama, div, options) { + var self = this, + args = [meta]; + options = options || {}; + + + //self.set('clickable', options.clickable === false ? false : true); + self.set('visible', options.visible === false ? false : true, true); + args.push(options); + + // Initial camera options + if ('camera' in options) { + self.set('camera', options.camera, true); + if ('zoom' in options.camera) { + self.set('camera_zoom', options.camera.zoom, true); + } + if ('bearing' in options.camera) { + self.set('camera_bearing', options.camera.bearing, true); + } + if ('tilt' in options.camera) { + self.set('camera_tilt', options.camera.tilt, true); + } + if ('target' in options.camera) { + self.set('position', options.camera.target, true); + } + } + + // Gesture options + options.gestures = options.gestures || {}; + options.gestures.panning = common.defaultTrueOption(options.gestures.panning); + options.gestures.zoom = common.defaultTrueOption(options.gestures.zoom); + self.set('gesture_panning', options.gestures.panning, true); + self.set('gesture_zoom', options.gestures.zoom, true); + + // Controls options + options.controls = options.controls || {}; + options.controls.navigation = common.defaultTrueOption(options.controls.navigation); + options.controls.streetNames = common.defaultTrueOption(options.controls.streetNames); + self.set('control_navigation', options.controls.navigation, true); + self.set('control_streetNames', options.controls.streetNames, true); + + // Gets the map div size. + // The plugin needs to consider the viewport zoom ratio + // for the case window.innerHTML > body.offsetWidth. + self.set('div', div); + var elemId = common.getPluginDomId(div); + args.push(elemId); + + exec.call({ + _isReady: true + }, function() { + + //------------------------------------------------------------------------ + // Clear background colors of map div parents after the map is created + //------------------------------------------------------------------------ + var positionCSS; + for (var i = 0; i < div.children.length; i++) { + positionCSS = common.getStyle(div.children[i], 'position'); + if (positionCSS === 'static') { + div.children[i].style.position = 'relative'; + } + } + //div.insertBefore(self._layers.info, div.firstChild); + + + while (div.parentNode) { + div.style.backgroundColor = 'rgba(0,0,0,0) !important'; + + common.attachTransparentClass(div); + + div = div.parentNode; + } + cordova.fireDocumentEvent('plugin_touch', { + force: true + }); + self._privateInitialize(); + delete self._privateInitialize; + + self.trigger(event.PANORAMA_READY, self); + }, self.errorHandler, 'CordovaGoogleMaps', 'getPanorama', args, { + sync: true + }); +}; + +StreetViewPanorama.prototype.setVisible = function(isVisible) { + this.set('visible', common.parseBoolean(isVisible)); + return this; +}; + +StreetViewPanorama.prototype.getVisible = function() { + return this.get('visible'); +}; + +StreetViewPanorama.prototype.getDiv = function() { + return this.get('div'); +}; +StreetViewPanorama.prototype.getMap = function() { + // stub +}; + +StreetViewPanorama.prototype.setPanningGesturesEnabled = function (boolValue) { + boolValue = common.parseBoolean(boolValue); + this.set('gesture_panning', boolValue); + return this; +}; +StreetViewPanorama.prototype.getPanningGesturesEnabled = function () { + return this.get('gesture_panning'); +}; +StreetViewPanorama.prototype.setZoomGesturesEnabled = function (boolValue) { + boolValue = common.parseBoolean(boolValue); + this.set('gesture_zoom', boolValue); + return this; +}; +StreetViewPanorama.prototype.getZoomGesturesEnabled = function () { + return this.get('gesture_zoom'); +}; +StreetViewPanorama.prototype.setNavigationEnabled = function (boolValue) { + boolValue = common.parseBoolean(boolValue); + this.set('control_navigation', boolValue); + return this; +}; +StreetViewPanorama.prototype.getNavigationEnabled = function () { + return this.get('control_navigation'); +}; +StreetViewPanorama.prototype.setStreetNamesEnabled = function (boolValue) { + boolValue = common.parseBoolean(boolValue); + this.set('control_streetNames', boolValue); + return this; +}; +StreetViewPanorama.prototype.getStreetNamesEnabled = function () { + return this.get('control_streetNames'); +}; +StreetViewPanorama.prototype.getLinks = function () { + return this.get('links'); +}; +StreetViewPanorama.prototype.getLocation = function () { + return this.get('location'); +}; +StreetViewPanorama.prototype.getPanoId = function () { + return this.get('pano'); +}; +StreetViewPanorama.prototype.getPosition = function () { + return this.get('position'); +}; + +StreetViewPanorama.prototype.setPosition = function(cameraPosition, callback) { + var self = this; + cameraPosition = cameraPosition || {}; + if (typeof cameraPosition === 'string') { + // case: panorama.setPosition(panoId); + cameraPosition = { + target : cameraPosition + }; + } + if (typeof cameraPosition.target === 'object') { + // case: panorama.setPosition({ + // target: {lat: ..., lng: ...} + // }); + self.set('position', cameraPosition.target); + } + self.exec.call(self, function() { + if (typeof callback === 'function') { + callback.call(self); + } + }, self.errorHandler, self.__pgmId, 'setPosition', [cameraPosition], { + sync: true + }); + return this; +}; + +StreetViewPanorama.prototype.setPov = function(pov, callback) { + var self = this; + pov = pov || {}; + if ('zoom' in pov) { + self.set('camera_zoom', pov.zoom); + } + if ('bearing' in pov) { + self.set('camera_bearing', pov.bearing); + } + if ('tilt' in pov) { + self.set('camera_tilt', pov.tilt); + } + self.exec.call(self, function() { + if (typeof callback === 'function') { + callback.call(self); + } + }, self.errorHandler, self.__pgmId, 'setPov', [pov], { + sync: true + }); + return this; +}; + + +StreetViewPanorama.prototype.remove = function(callback) { + var self = this; + + var resolver = function(resolve, reject) { + self.exec.call(self, + function() { + self.destroy(); + resolve.call(self); + }, + reject.bind(self), + 'CordovaGoogleMaps', 'removeMap', [self.__pgmId], { + sync: true, + remove: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + + + + + + + + + self.trigger('remove'); +}; +StreetViewPanorama.prototype._onPanoramaCameraChange = function(eventName, cameraPosition) { + var self = this; + self.set('camera', utils.clone(self.get('camera') || {}, cameraPosition)); + self.set('camera_zoom', cameraPosition.zoom); + self.set('camera_bearing', cameraPosition.bearing); + self.set('camera_tilt', cameraPosition.viewAngle || cameraPosition.tilt); + self.set('links', cameraPosition.links || []); + if (self._isReady) { + self._onPanoramaEvent(eventName, cameraPosition); + } +}; + +StreetViewPanorama.prototype._onPanoramaLocationChange = function(eventName, panoramaLocation) { + var self = this; + self.set('location', panoramaLocation); + self.set('pano', panoramaLocation ? panoramaLocation.panoId : null); + self.set('position', panoramaLocation ? panoramaLocation.latLng : null); + if (self._isReady) { + self._onPanoramaEvent(eventName, panoramaLocation); + } +}; + +StreetViewPanorama.prototype._onPanoramaEvent = function() { + var self = this; + if (self._isReady) { + var args = Array.prototype.slice.call(arguments, 0); + args.push(self); + self.trigger.apply(self, args); + } +}; + +module.exports = StreetViewPanorama; diff --git a/node_modules/cordova-plugin-googlemaps/www/Thread.js b/node_modules/cordova-plugin-googlemaps/www/Thread.js new file mode 100644 index 0000000..278023f --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/Thread.js @@ -0,0 +1,381 @@ +// Thread.js +// https://threadjs.deep-rain.com +// https://github.com/deep-rain/thread +// Apache License +// Version 2.0, January 2004 +// http://www.apache.org/licenses/ +// +// TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +// +// 1. Definitions. +// +// 'License' shall mean the terms and conditions for use, reproduction, +// and distribution as defined by Sections 1 through 9 of this document. +// +// 'Licensor' shall mean the copyright owner or entity authorized by +// the copyright owner that is granting the License. +// +// 'Legal Entity' shall mean the union of the acting entity and all +// other entities that control, are controlled by, or are under common +// control with that entity. For the purposes of this definition, +// 'control' means (i) the power, direct or indirect, to cause the +// direction or management of such entity, whether by contract or +// otherwise, or (ii) ownership of fifty percent (50%) or more of the +// outstanding shares, or (iii) beneficial ownership of such entity. +// +// 'You' (or 'Your') shall mean an individual or Legal Entity +// exercising permissions granted by this License. +// +// 'Source' form shall mean the preferred form for making modifications, +// including but not limited to software source code, documentation +// source, and configuration files. +// +// 'Object' form shall mean any form resulting from mechanical +// transformation or translation of a Source form, including but +// not limited to compiled object code, generated documentation, +// and conversions to other media types. +// +// 'Work' shall mean the work of authorship, whether in Source or +// Object form, made available under the License, as indicated by a +// copyright notice that is included in or attached to the work +// (an example is provided in the Appendix below). +// +// 'Derivative Works' shall mean any work, whether in Source or Object +// form, that is based on (or derived from) the Work and for which the +// editorial revisions, annotations, elaborations, or other modifications +// represent, as a whole, an original work of authorship. For the purposes +// of this License, Derivative Works shall not include works that remain +// separable from, or merely link (or bind by name) to the interfaces of, +// the Work and Derivative Works thereof. +// +// 'Contribution' shall mean any work of authorship, including +// the original version of the Work and any modifications or additions +// to that Work or Derivative Works thereof, that is intentionally +// submitted to Licensor for inclusion in the Work by the copyright owner +// or by an individual or Legal Entity authorized to submit on behalf of +// the copyright owner. For the purposes of this definition, 'submitted' +// means any form of electronic, verbal, or written communication sent +// to the Licensor or its representatives, including but not limited to +// communication on electronic mailing lists, source code control systems, +// and issue tracking systems that are managed by, or on behalf of, the +// Licensor for the purpose of discussing and improving the Work, but +// excluding communication that is conspicuously marked or otherwise +// designated in writing by the copyright owner as 'Not a Contribution.' +// +// 'Contributor' shall mean Licensor and any individual or Legal Entity +// on behalf of whom a Contribution has been received by Licensor and +// subsequently incorporated within the Work. +// +// 2. Grant of Copyright License. Subject to the terms and conditions of +// this License, each Contributor hereby grants to You a perpetual, +// worldwide, non-exclusive, no-charge, royalty-free, irrevocable +// copyright license to reproduce, prepare Derivative Works of, +// publicly display, publicly perform, sublicense, and distribute the +// Work and such Derivative Works in Source or Object form. +// +// 3. Grant of Patent License. Subject to the terms and conditions of +// this License, each Contributor hereby grants to You a perpetual, +// worldwide, non-exclusive, no-charge, royalty-free, irrevocable +// (except as stated in this section) patent license to make, have made, +// use, offer to sell, sell, import, and otherwise transfer the Work, +// where such license applies only to those patent claims licensable +// by such Contributor that are necessarily infringed by their +// Contribution(s) alone or by combination of their Contribution(s) +// with the Work to which such Contribution(s) was submitted. If You +// institute patent litigation against any entity (including a +// cross-claim or counterclaim in a lawsuit) alleging that the Work +// or a Contribution incorporated within the Work constitutes direct +// or contributory patent infringement, then any patent licenses +// granted to You under this License for that Work shall terminate +// as of the date such litigation is filed. +// +// 4. Redistribution. You may reproduce and distribute copies of the +// Work or Derivative Works thereof in any medium, with or without +// modifications, and in Source or Object form, provided that You +// meet the following conditions: +// +// (a) You must give any other recipients of the Work or +// Derivative Works a copy of this License; and +// +// (b) You must cause any modified files to carry prominent notices +// stating that You changed the files; and +// +// (c) You must retain, in the Source form of any Derivative Works +// that You distribute, all copyright, patent, trademark, and +// attribution notices from the Source form of the Work, +// excluding those notices that do not pertain to any part of +// the Derivative Works; and +// +// (d) If the Work includes a 'NOTICE' text file as part of its +// distribution, then any Derivative Works that You distribute must +// include a readable copy of the attribution notices contained +// within such NOTICE file, excluding those notices that do not +// pertain to any part of the Derivative Works, in at least one +// of the following places: within a NOTICE text file distributed +// as part of the Derivative Works; within the Source form or +// documentation, if provided along with the Derivative Works; or, +// within a display generated by the Derivative Works, if and +// wherever such third-party notices normally appear. The contents +// of the NOTICE file are for informational purposes only and +// do not modify the License. You may add Your own attribution +// notices within Derivative Works that You distribute, alongside +// or as an addendum to the NOTICE text from the Work, provided +// that such additional attribution notices cannot be construed +// as modifying the License. +// +// You may add Your own copyright statement to Your modifications and +// may provide additional or different license terms and conditions +// for use, reproduction, or distribution of Your modifications, or +// for any such Derivative Works as a whole, provided Your use, +// reproduction, and distribution of the Work otherwise complies with +// the conditions stated in this License. +// +// 5. Submission of Contributions. Unless You explicitly state otherwise, +// any Contribution intentionally submitted for inclusion in the Work +// by You to the Licensor shall be under the terms and conditions of +// this License, without any additional terms or conditions. +// Notwithstanding the above, nothing herein shall supersede or modify +// the terms of any separate license agreement you may have executed +// with Licensor regarding such Contributions. +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor, +// except as required for reasonable and customary use in describing the +// origin of the Work and reproducing the content of the NOTICE file. +// +// 7. Disclaimer of Warranty. Unless required by applicable law or +// agreed to in writing, Licensor provides the Work (and each +// Contributor provides its Contributions) on an 'AS IS' BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied, including, without limitation, any warranties or conditions +// of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +// PARTICULAR PURPOSE. You are solely responsible for determining the +// appropriateness of using or redistributing the Work and assume any +// risks associated with Your exercise of permissions under this License. +// +// 8. Limitation of Liability. In no event and under no legal theory, +// whether in tort (including negligence), contract, or otherwise, +// unless required by applicable law (such as deliberate and grossly +// negligent acts) or agreed to in writing, shall any Contributor be +// liable to You for damages, including any direct, indirect, special, +// incidental, or consequential damages of any character arising as a +// result of this License or out of the use or inability to use the +// Work (including but not limited to damages for loss of goodwill, +// work stoppage, computer failure or malfunction, or any and all +// other commercial damages or losses), even if such Contributor +// has been advised of the possibility of such damages. +// +// 9. Accepting Warranty or Additional Liability. While redistributing +// the Work or Derivative Works thereof, You may choose to offer, +// and charge a fee for, acceptance of support, warranty, indemnity, +// or other liability obligations and/or rights consistent with this +// License. However, in accepting such obligations, You may act only +// on Your own behalf and on Your sole responsibility, not on behalf +// of any other Contributor, and only if You agree to indemnify, +// defend, and hold each Contributor harmless for any liability +// incurred by, or claims asserted against, such Contributor by reason +// of your accepting any such warranty or additional liability. +// +// END OF TERMS AND CONDITIONS +// +// APPENDIX: How to apply the Apache License to your work. +// +// To apply the Apache License to your work, attach the following +// boilerplate notice, with the fields enclosed by brackets '{}' +// replaced with your own identifying information. (Don't include +// the brackets!) The text should be enclosed in the appropriate +// comment syntax for the file format. We also recommend that a +// file or class name and description of purpose be included on the +// same 'printed page' as the copyright notice for easier +// identification within third-party archives. +// +// Copyright 2017 deep-rain.com +// +// Licensed under the Apache License, Version 2.0 (the 'License'); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an 'AS IS' BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +// Constants +var Type = { + RESOLVE: 'resolve', + NOTIFY: 'notify' +}; +var REPLACE_SEQUENCE = '{{#CODE}}'; +var CODE = 'onmessage = function(e){ postMessage({type:"' + Type.RESOLVE + '",content:({{#CODE}}).call(this, (e.data ? JSON.parse(e.data) : undefined))});};function notify(c){postMessage({type:"' + Type.NOTIFY + '", content:c})}'; + +var getEmbeddedCode = function (s) { + return CODE.replace(REPLACE_SEQUENCE, s); +}; + +module.exports = (function () { + + // Deferred class + var Deferred = function () { + this.resolveQueues = []; + this.rejectQueues = []; + this.progressQueues = []; + this.args = null; + this.isDone = false; + this.isFailed = false; + }; + + Deferred.prototype.done = function (func) { + if (this.isDone) { + func(this.args); + } else { + this.resolveQueues.push(func); + } + return this; + }; + + Deferred.prototype.fail = function (func) { + if (this.isFailed) { + func(this.args); + } else { + this.rejectQueues.push(func); + } + return this; + }; + + Deferred.prototype.always = function (func) { + if (this.isFailed || this.isDone) { + func(this.args); + } else { + this.resolveQueues.push(func); + this.rejectQueues.push(func); + } + return this; + }; + + Deferred.prototype.progress = function (func) { + this.progressQueues.push(func); + }; + + Deferred.prototype.resolve = function (obj) { + this.isDone = true; + this.args = obj; + this.resolveQueues.forEach(function (q) { + q(obj); + }); + return this; + }; + + Deferred.prototype.notify = function (obj) { + this.progressQueues.forEach(function (q) { + q(obj); + }); + }; + + Deferred.prototype.reject = function (obj) { + this.isFailed = true; + this.args = obj; + this.rejectQueues.forEach(function (q) { + q(obj); + }); + return this; + }; + + // Thread class + var Thread = function (func, depends) { + + // check browser + if (!window.Blob || + !window.URL || + !window.Worker) { + throw new Error('this browser is not supported'); + } + + // inject dependent libraries. + depends = depends || []; + if (!(depends instanceof window.Array)) { + depends = [depends]; + } + + var scripts = ''; + if (depends.length > 0) { + scripts = 'importScripts("' + depends.join('','') + '");'; + } + + // create Blob object. + var blob = new window.Blob([scripts + getEmbeddedCode(func.toString())]); + this.blobUrl = window.URL.createObjectURL(blob); + + // greate Worker object. + this.worker = new window.Worker(this.blobUrl); + this.deferred = new Deferred(); + + // add event listener. + var self = this; + + // onmessage + this.worker.addEventListener('message', function (e) { + switch (e.data.type) { + case Type.RESOLVE: + self.deferred.resolve(e.data.content); + return; + case Type.NOTIFY: + self.deferred.notify(e.data.content); + return; + } + }); + + // onerror + this.worker.addEventListener('error', function (e) { + self.deferred.reject(e); + }); + }; + + Thread.prototype.execute = function (obj) { + if (this.isClosed) { + throw new Error('thread has been closed.'); + } + + var json = JSON.stringify(obj); + this.worker.postMessage(json); + return this; + }; + + Thread.prototype.once = function (args) { + var self = this; + this.deferred.always(function () { + self.terminate(); + }); + this.execute(args); + this.isClosed = true; + return this; + }; + + Thread.prototype.terminate = function () { + window.URL.revokeObjectURL(this.blobUrl); + this.worker.terminate(); + this.worker = null; + this.deferred = null; + this.blobUrl = null; + }; + + Thread.prototype.done = function (f) { + this.deferred.done(f); + return this; + }; + + Thread.prototype.fail = function (f) { + this.deferred.fail(f); + return this; + }; + + Thread.prototype.progress = function (f) { + this.deferred.progress(f); + return this; + }; + + return Thread; +})(); diff --git a/node_modules/cordova-plugin-googlemaps/www/TileOverlay.js b/node_modules/cordova-plugin-googlemaps/www/TileOverlay.js new file mode 100644 index 0000000..f7999dd --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/TileOverlay.js @@ -0,0 +1,119 @@ +var utils = require('cordova/utils'), + common = require('./Common'), + Overlay = require('./Overlay'); + +/***************************************************************************** + * TileOverlay Class + *****************************************************************************/ +var TileOverlay = function (map, tileOverlayOptions, _exec) { + Overlay.call(this, map, tileOverlayOptions, 'TileOverlay', _exec); + + var self = this; + + //----------------------------------------------- + // Sets event listeners + //----------------------------------------------- + self.on('fadeIn_changed', function () { + var fadeIn = self.get('fadeIn'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setFadeIn', [self.getId(), fadeIn]); + }); + self.on('opacity_changed', function () { + var opacity = self.get('opacity'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setOpacity', [self.getId(), opacity]); + }); + self.on('zIndex_changed', function () { + var zIndex = self.get('zIndex'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setZIndex', [self.getId(), zIndex]); + }); + self.on('visible_changed', function () { + var visible = self.get('visible'); + self.exec.call(self, null, self.errorHandler, self.getPluginName(), 'setVisible', [self.getId(), visible]); + }); +}; + +utils.extend(TileOverlay, Overlay); + +TileOverlay.prototype.getPluginName = function () { + return this.map.getId() + '-tileoverlay'; +}; + +TileOverlay.prototype.getHashCode = function () { + return this.hashCode; +}; + +TileOverlay.prototype.getMap = function () { + return this.map; +}; +TileOverlay.prototype.getId = function () { + return this.__pgmId; +}; +TileOverlay.prototype.getTileSize = function () { + return this.get('tileSize'); +}; +TileOverlay.prototype.getZIndex = function () { + return this.get('zIndex'); +}; +TileOverlay.prototype.setZIndex = function (zIndex) { + this.set('zIndex', zIndex); +}; +TileOverlay.prototype.setFadeIn = function (fadeIn) { + fadeIn = common.parseBoolean(fadeIn); + this.set('fadeIn', fadeIn); +}; +TileOverlay.prototype.getFadeIn = function () { + return this.get('fadeIn'); +}; +TileOverlay.prototype.setVisible = function (visible) { + visible = common.parseBoolean(visible); + this.set('visible', visible); +}; +TileOverlay.prototype.getOpacity = function () { + return this.get('opacity'); +}; +TileOverlay.prototype.setOpacity = function (opacity) { + if (!opacity && opacity !== 0) { + console.log('opacity value must be int or double'); + return false; + } + this.set('opacity', opacity); +}; +TileOverlay.prototype.getVisible = function () { + return this.get('visible'); +}; + +TileOverlay.prototype.remove = function (callback) { + var self = this; + if (self._isRemoved) { + if (typeof callback === 'function') { + return; + } else { + return Promise.resolve(); + } + } + Object.defineProperty(self, '_isRemoved', { + value: true, + writable: false + }); + self.trigger(self.__pgmId + '_remove'); + + var resolver = function(resolve, reject) { + self.exec.call(self, + function() { + self.destroy(); + resolve.call(self); + }, + reject.bind(self), + self.getPluginName(), 'remove', [self.getId()], { + remove: true + }); + }; + + if (typeof callback === 'function') { + resolver(callback, self.errorHandler); + } else { + return new Promise(resolver); + } + +}; + +module.exports = TileOverlay; diff --git a/node_modules/cordova-plugin-googlemaps/www/VisibleRegion.js b/node_modules/cordova-plugin-googlemaps/www/VisibleRegion.js new file mode 100644 index 0000000..d04c9a2 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/VisibleRegion.js @@ -0,0 +1,66 @@ +var utils = require('cordova/utils'), + LatLngBounds = require('./LatLngBounds'); + +/***************************************************************************** + * VisibleRegion Class + *****************************************************************************/ +var VisibleRegion = function(southwest, northeast, farLeft, farRight, nearLeft, nearRight) { + Object.defineProperty(this, 'type', { + value: 'VisibleRegion', + writable: false + }); + this.southwest = southwest; + this.northeast = northeast; + this.farLeft = farLeft; + this.farRight = farRight; + this.nearLeft = nearLeft; + this.nearRight = nearRight; +}; + +utils.extend(VisibleRegion, LatLngBounds); + +delete VisibleRegion.prototype.extend; +delete VisibleRegion.prototype.getCenter; + +VisibleRegion.prototype.contains = function(latLng) { + if (!latLng || !('lat' in latLng) || !('lng' in latLng)) { + return false; + } + var y = latLng.lat, + x = latLng.lng; + + var y90 = y + 90; + var south = this.southwest.lat, + north = this.northeast.lat, + west = this.southwest.lng, + east = this.northeast.lng; + var south90 = south + 90, + north90 = north + 90; + + var containX = false, + containY = false; + + if (east >= 0 && west >= east) { + if (x <= 0 && x >= -180) { + containX = true; + } else { + containX = (west <= x && x <= east); + } + } else if (west <= 0 && east <= west) { + containX = (west <= x && x <= east); + if (x >= 0 && x <= 180) { + containX = true; + } else { + containX = (x <= 0 && x <= west || x <= east && x>= -180); + } + } else { + return LatLngBounds.prototype.contains.call(this, latLng); + } + + containY = (south90 <= y90 && y90 <= north90) || //#a + (south >= 0 && north <= 0 && ((south <= y && y <= 90) || (y >= -90 && y<= north))); // #d + + return containX && containY; +}; + +module.exports = VisibleRegion; diff --git a/node_modules/cordova-plugin-googlemaps/www/__test__/BaseArrayClass.spec.js b/node_modules/cordova-plugin-googlemaps/www/__test__/BaseArrayClass.spec.js new file mode 100644 index 0000000..03b873b --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/__test__/BaseArrayClass.spec.js @@ -0,0 +1,267 @@ +// jshint esversion: 6 +const BaseArrayClass = require('../BaseArrayClass'); + +describe('[BaseArrayClass]', () => { + + describe('getLength()', () => { + it('should return the same size with initial array', () => { + const initArray = [0, 1, 2]; + const _ = new BaseArrayClass(initArray); + expect(_.getLength()).toBe(initArray.length); + }); + }); + + + describe('mapSeries()', () => { + it('should keep item order', () => { + const initArray = ['A', 'B', 'C']; + const _ = new BaseArrayClass(initArray); + expect(_.mapSeries((item, next) => { + setTimeout(() => { + next(item); + }, 3 * Math.random()); + })).resolves.toEqual(initArray); + }); + }); + + describe('mapAsync()', () => { + it('should keep item order', () => { + const initArray = ['A', 'B', 'C']; + const _ = new BaseArrayClass(initArray); + expect(_.mapAsync((item, next) => { + setTimeout(() => { + next(item); + }, 3 * Math.random()); + })).resolves.toEqual(initArray); + }); + }); + + describe('map()', () => { + it('should work as `mapAsync()` if callback is given', (done) => { + const initArray = ['A', 'B', 'C']; + const _ = new BaseArrayClass(initArray); + _.map((item, next) => { + setTimeout(() => { + next(item); + }, 3 * Math.random()); + }, (results) => { + expect(results).toEqual(initArray); + done(); + }); + }); + + it('should work as `Array.map()` if callback is omitted', () => { + const initArray = ['A', 'B', 'C']; + const _ = new BaseArrayClass(initArray); + expect(_.map((item) => { + return item; + })).toEqual(initArray); + }); + }); + + describe('forEachAsync()', () => { + it('should execute `iteratee` 3 times', (done) => { + const initArray = ['A', 'B', 'C']; + const _ = new BaseArrayClass(initArray); + let i = 0; + _.forEachAsync((item, next) => { + setTimeout(() => { + i++; + next(item); + }, 0); + }).then(() => { + expect(i).toBe(3); + done(); + }); + }); + }); + + describe('forEach()', () => { + it('should work as `forEach()` if callback is given', (done) => { + let i = 0; + const initArray = ['A', 'B', 'C']; + const _ = new BaseArrayClass(initArray); + _.forEach((item, next) => { + setTimeout(() => { + i++; + next(); + }, 3 * Math.random()); + }, () => { + expect(i).toBe(3); + done(); + }); + }); + + it('should work as `Array.forEach()` if callback is omitted', () => { + let i = 0; + const initArray = ['A', 'B', 'C']; + const _ = new BaseArrayClass(initArray); + _.forEach((item) => { + i++; + }); + expect(i).toBe(3); + }); + }); + + describe('filterAsync()', () => { + it('should return [0, 2, 4]', (done) => { + const initArray = [0, 1, 2, 3, 4, 5]; + const _ = new BaseArrayClass(initArray); + _.filterAsync((item, next) => { + setTimeout(() => { + next(item % 2 === 0); + }, 5 * Math.random()); + }).then((results) => { + expect(results).toEqual([0, 2, 4]); + done(); + }); + }); + }); + + describe('filter()', () => { + it('should work as `filter()` if callback is given', (done) => { + let i = 0; + const initArray = [0, 1, 2, 3, 4, 5]; + const _ = new BaseArrayClass(initArray); + _.filter((item, next) => { + setTimeout(() => { + next(item % 2 !== 0); + }, 3 * Math.random()); + }, (results) => { + expect(results).toEqual([1, 3, 5]); + done(); + }); + }); + + it('should work as `Array.filter()` if callback is omitted', () => { + const initArray = [ + {condition: true}, + {condition: false}, + {condition: true} + ]; + const _ = new BaseArrayClass(initArray); + expect(_.filter((item) => { + return item.condition === true; + })).toHaveLength(2); + }); + }); + + describe('indexOf()', () => { + it('should find the first element position', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + expect(_.indexOf('World')).toBe(1); + }); + + it('should find the second element position', () => { + const initArray = ['Hello', 'World', 'test', 'World']; + const _ = new BaseArrayClass(initArray); + expect(_.indexOf('World', 2)).toBe(3); + }); + }); + + describe('empty()', () => { + it('should delete all elements', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + _.empty(); + expect(_.getLength()).toBe(0); + }); + + }); + + describe('push()', () => { + it('should add the item to the last', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + _.push('HelloWorld'); + expect(_.getAt(3)).toEqual('HelloWorld'); + }); + }); + + describe('insertAt()', () => { + it('should add the item at the specified position', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + _.insertAt(1, 'Aloha'); + expect(_.getArray()).toEqual(['Hello', 'Aloha', 'World', 'test']); + }); + }); + + + describe('getArray()', () => { + it('should return the same array', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + expect(_.getArray()).toEqual(['Hello', 'World', 'test']); + }); + }); + + describe('getAt()', () => { + it('should return the same item at specified position', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + expect(_.getAt(1)).toEqual('World'); + }); + }); + + describe('setAt()', () => { + it('should replace the item of specified position', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + _.setAt(0, 'Aloha'); + expect(_.getAt(0)).toEqual('Aloha'); + }); + }); + + describe('removeAt()', () => { + it('should remove the item of specified position', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + _.removeAt(1); + expect(_.getAt(1)).toEqual('test'); + expect(_.getLength()).toBe(2); + }); + }); + + describe('pop()', () => { + it('should return the same item of the last item', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + expect(_.pop()).toEqual('test'); + expect(_.getLength()).toBe(2); + }); + }); + + + describe('reverse()', () => { + it('should reverse the array', () => { + const initArray = ['Hello', 'World', 'test']; + const _ = new BaseArrayClass(initArray); + _.reverse(); + expect(_.getArray()).toEqual(['test', 'World', 'Hello']); + }); + }); + + describe('sort()', () => { + it('should return ["A", "B", "C", "a", "b", "c"]', () => { + const initArray = ['c', 'C', 'A', 'B', 'b', 'a']; + const _ = new BaseArrayClass(initArray); + _.sort(); + expect(_.getArray()).toEqual(['A', 'B', 'C', 'a', 'b', 'c']); + }); + + it('should return ["a", "A", "b", "B", "c", "C"]', () => { + const initArray = ['c', 'C', 'A', 'B', 'b', 'a']; + const _ = new BaseArrayClass(initArray); + _.sort((a, b) => { + const charCodeA = a.charCodeAt(0); + const charCodeB = b.charCodeAt(0); + const lowerA = charCodeA > 96 ? charCodeA - 32 : charCodeA; + const lowerB = charCodeB > 96 ? charCodeB - 32 : charCodeB; + return lowerA === lowerB ? charCodeB - charCodeA : lowerA - lowerB; + }); + expect(_.getArray()).toEqual(['a', 'A', 'b', 'B', 'c', 'C']); + }); + }); +}); diff --git a/node_modules/cordova-plugin-googlemaps/www/__test__/BaseClass.spec.js b/node_modules/cordova-plugin-googlemaps/www/__test__/BaseClass.spec.js new file mode 100644 index 0000000..1ef4660 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/__test__/BaseClass.spec.js @@ -0,0 +1,186 @@ +// jshint esversion: 6 +const BaseClass = require('../BaseClass'); + +describe('[BaseClass]', () => { + + it('"instanceB.hello" should be "world"', () => { + const instance = new BaseClass(); + instance.set('hello', 'world'); + expect(instance.get('hello')).toEqual('world'); + }); + + it('"instanceB.anotherHello" should be the same as "instanceA.hello"', () => { + const instanceA = new BaseClass(); + const instanceB = new BaseClass(); + instanceA.bindTo('hello', instanceB, 'anotherHello'); + instanceA.set('hello', 'world'); + expect(instanceA.get('hello')).toEqual('world'); + expect(instanceB.get('anotherHello')).toEqual('world'); + }); + + + it('"instance.one()" should receive "hello_changed" event.', (done) => { + const instance = new BaseClass(); + instance.set('hello', 'world'); + + instance.one('hello_changed', (prevValue, newValue) => { + expect(prevValue).toEqual('world'); + expect(newValue).toEqual('Aloha'); + done(); + }); + instance.set('hello', 'Aloha'); + }); + + it('"instance.hasEventListener(\'hello_changed\')"', () => { + const instance = new BaseClass(); + + instance.on('hello_changed', () => { }); + expect(instance.hasEventListener('hello_changed')).toEqual(true); + + instance.off('hello_changed'); + expect(instance.hasEventListener('hello_changed')).toEqual(false); + }); + + it('"instance.on()" should receive "hello_changed" event twice.', (done) => { + (new Promise((resolve, reject) => { + const instance = new BaseClass(); + + const timer = setTimeout(reject, 50); // just in case + + let count = 0; + const listener = (prevValue, newValue) => { + count++; + if (count === 2) { + instance.off('hello_changed', listener); + clearTimeout(timer); + resolve(newValue); + } + }; + instance.on('hello_changed', listener); + + instance.set('hello', '你好'); // should receive + instance.set('hello', 'こんにちは'); // should receive + instance.set('hello', '안녕하세요'); // should not receive + })) + .then((answer) => { + expect(answer).toBe('こんにちは'); + done(); + }); + }); + + it('"instance.on() then .off()" should receive only one time "hello_changed" event.', (done) => { + (new Promise((resolve, reject) => { + const instance = new BaseClass(); + let count = 0; + const listener = () => { + count++; + instance.off('hello_changed', listener); + }; + instance.on('hello_changed', listener); + + setTimeout(() => { + resolve(count); + }, 10); + + for (let i = 0; i < 10; i++) { + instance.set('hello', i); + } + })) + .then((answer) => { + expect(answer).toBe(1); + done(); + }); + }); + + it('"instance.off()" should remove all event listeners.', (done) => { + (new Promise((resolve, reject) => { + const instance = new BaseClass(); + let called = false; + const dummyListener = () => { + called = true; + }; + + instance.on('myEvent', dummyListener); + instance.on('myEvent', dummyListener); + instance.on('myEvent', dummyListener); + instance.on('myEvent', dummyListener); + instance.off('myEvent'); + instance.trigger('myEvent'); + + setTimeout(() => { + resolve(called); + }, 10); + + })) + .then((answer) => { + expect(answer).toBe(false); + done(); + }); + }); + + it('"instance.onThrottled()" should receive event only 3 times.', (done) => { + (new Promise((resolve) => { + + let eventCount = 0; + const instance = new BaseClass(); + + // eventListener should be involved 3 times. + // first time: at 50ms + // second time: at 100ms + // third time: at 150ms + instance.onThrottled('myEvent', (receivedData) => { + eventCount++; + }, 50); + + // Trigger event 13 times (takes 130ms) + let sendCount = 0; + const triggerTimer = setInterval(() => { + sendCount++; + instance.trigger('myEvent', sendCount); + if (sendCount > 12) { + clearInterval(triggerTimer); + } + }, 10); + + // When 300ms, test should be complited. + setTimeout(() => { + resolve(eventCount); + }, 300); + + })) + .then((answer) => { + expect(answer).toBe(3); + done(); + }); + }); + + it('"instance.trigger()" should fire a "myEvent" event.', (done) => { + (new Promise((resolve, reject) => { + + const timer = setTimeout(reject, 10); // just in case + const instance = new BaseClass(); + + instance.on('myEvent', (...parameters) => { + clearTimeout(timer); + resolve(parameters); + }); + + instance.trigger('myEvent', 'data', 1); + })) + .then((receivedData) => { + expect(receivedData[0]).toBe('data'); + expect(receivedData[1]).toBe(1); + done(); + }); + }); + + it('"instance.empty()" should delete all holded variables.', () => { + const instance = new BaseClass(); + instance.set('hello1', 'world'); + instance.set('hello2', 'test'); + instance.empty(); + expect(instance.get('hello1')).toBe(undefined); + expect(instance.get('hello2')).toBe(undefined); + }); + +}); diff --git a/node_modules/cordova-plugin-googlemaps/www/__test__/Geocoder.spec.js b/node_modules/cordova-plugin-googlemaps/www/__test__/Geocoder.spec.js new file mode 100644 index 0000000..8149e82 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/__test__/Geocoder.spec.js @@ -0,0 +1,120 @@ +// jshint esversion: 6 +const Geocoder = require('../Geocoder'); + +describe('Geocode', () => { + // Initialize them here so that vscode typing + // will work without this being a TS project + let geocoder = new Geocoder(); + let execSpy = jest.fn(); + + beforeEach(() => { + execSpy = jest.fn(); + geocoder = new Geocoder(execSpy); + }); + + it('should exist', () => { + expect(geocoder).toBeDefined(); + }); + + describe('geocode()', () => { + it('should exist', () => { + expect(geocoder.geocode).toBeDefined(); + }); + + it('should return a promise if a callback is not passed', async () => { + const request = { + address: '', + }; + const mockResults = [ + { + foo: 'bar', + }, + ]; + execSpy.mockImplementation(resolve => { + resolve({ + results: mockResults, + }); + }); + + const results = geocoder.geocode(request); + expect(results).toMatchSnapshot(); + await expect(results).resolves.toMatchSnapshot(); + }); + + it('should error if both position and address options are provided', async () => { + const request = { + position: [], + address: [], + }; + + await expect(geocoder.geocode(request)).rejects.toMatchSnapshot(); + }); + + it('should call the if both position and address options are provided', () => { + const request = { + position: [], + address: [], + }; + + expect(geocoder.geocode(request)).rejects.toMatchSnapshot(); + }); + + describe('single request', () => { + describe('position', () => { + it('should call the native plugin with the correct parameters', () => { + geocoder.geocode({ + position: { + lat: 0, + lng: 0, + }, + }); + expect(execSpy.mock.calls).toMatchSnapshot(); + }); + }); + + describe('address', () => { + it('should call the native plugin with the correct parameters', () => { + geocoder.geocode({ + address: 'Tokyo, Japan', + }); + expect(execSpy.mock.calls).toMatchSnapshot(); + }); + }); + }); + + + + describe('multiple request', () => { + describe('position', () => { + it('should call the native plugin with the correct parameters', () => { + geocoder.geocode({ + position: [ + { + lat: 0, + lng: 0, + }, + { + lat: 1, + lng: 1, + }, + ] + }); + expect(execSpy.mock.calls).toMatchSnapshot(); + }); + }); + + describe('address1', () => { + it('should call the native plugin with the correct parameters', () => { + geocoder.geocode({ + address: [ + 'Tokyo, Japan', + 'Kyoto, Japan', + ] + }); + expect(execSpy.mock.calls).toMatchSnapshot(); + }); + }); + }); + + }); +}); diff --git a/node_modules/cordova-plugin-googlemaps/www/__test__/__snapshots__/Geocoder.spec.js.snap b/node_modules/cordova-plugin-googlemaps/www/__test__/__snapshots__/Geocoder.spec.js.snap new file mode 100644 index 0000000..23af879 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/__test__/__snapshots__/Geocoder.spec.js.snap @@ -0,0 +1,116 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Geocode geocode() multiple request address1 should call the native plugin with the correct parameters 1`] = ` +Array [ + Array [ + [Function], + [Function], + "PluginGeocoder", + "geocode", + Array [ + Object { + "address": "Tokyo, Japan", + "idx": 0, + }, + ], + ], + Array [ + [Function], + [Function], + "PluginGeocoder", + "geocode", + Array [ + Object { + "address": "Kyoto, Japan", + "idx": 1, + }, + ], + ], +] +`; + +exports[`Geocode geocode() multiple request position should call the native plugin with the correct parameters 1`] = ` +Array [ + Array [ + [Function], + [Function], + "PluginGeocoder", + "geocode", + Array [ + Object { + "idx": 0, + "position": Object { + "lat": 0, + "lng": 0, + }, + }, + ], + ], + Array [ + [Function], + [Function], + "PluginGeocoder", + "geocode", + Array [ + Object { + "idx": 1, + "position": Object { + "lat": 1, + "lng": 1, + }, + }, + ], + ], +] +`; + +exports[`Geocode geocode() should call the if both position and address options are provided 1`] = `[Error: Invalid request]`; + +exports[`Geocode geocode() should error if both position and address options are provided 1`] = `[Error: Invalid request]`; + +exports[`Geocode geocode() should return a promise if a callback is not passed 1`] = `Promise {}`; + +exports[`Geocode geocode() should return a promise if a callback is not passed 2`] = ` +Array [ + Object { + "foo": "bar", + }, +] +`; + +exports[`Geocode geocode() single request address should call the native plugin with the correct parameters 1`] = ` +Array [ + Array [ + [Function], + [Function], + "PluginGeocoder", + "geocode", + Array [ + Object { + "address": "Tokyo, Japan", + "idx": -1, + }, + ], + ], +] +`; + +exports[`Geocode geocode() single request position should call the native plugin with the correct parameters 1`] = ` +Array [ + Array [ + [Function], + [Function], + "PluginGeocoder", + "geocode", + Array [ + Object { + "idx": -1, + "position": Object { + "lat": 0, + "lng": 0, + }, + }, + ], + ], +] +`; diff --git a/node_modules/cordova-plugin-googlemaps/www/commandQueueExecutor.js b/node_modules/cordova-plugin-googlemaps/www/commandQueueExecutor.js new file mode 100644 index 0000000..38885d9 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/commandQueueExecutor.js @@ -0,0 +1,185 @@ +/***************************************************************************** + * Command queue mechanism + * (Save the number of method executing at the same time) + *****************************************************************************/ +var cordova_exec = require('cordova/exec'), + common = require('./Common'); + +var commandQueue = []; +var _isWaitMethod = null; +var _isExecuting = false; +var _executingCnt = 0; +var MAX_EXECUTE_CNT = 10; +var _lastGetMapExecuted = 0; +var _isResizeMapExecuting = false; + +// This flag becomes true when the page will be unloaded. +var _stopRequested = false; + + +function execCmd(success, error, pluginName, methodName, args, execOptions) { + execOptions = execOptions || {}; + + // The JavaScript special keyword 'this' indicates `who call this function`. + // This execCmd function is executed from overlay instances such as marker. + // So `this` means `overlay` instance. + var overlay = this; + + // If the overlay has been already removed from map, + // do not execute any methods on it. + if (overlay._isRemoved && !execOptions.remove) { + console.error('[ignore]' + pluginName + '.' + methodName + ', because removed.'); + return true; + } + + // If the overlay is not ready in native side, + // do not execute any methods except remove on it. + // This code works for map class especially. + if (!this._isReady && methodName !== 'remove') { + console.error('[ignore]' + pluginName + '.' + methodName + ', because it\'s not ready.'); + return true; + } + + // Push the method into the commandQueue(FIFO) at once. + commandQueue.push({ + 'execOptions': execOptions, + 'args': [ + function() { + //------------------------------- + // success callback + //------------------------------- + + if (methodName === 'resizeMap') { + _isResizeMapExecuting = false; + } + + // Even if the method was successful, + // but the _stopRequested flag is true, + // do not execute further code. + if (!_stopRequested && success) { + var results = Array.prototype.slice.call(arguments, 0); + common.nextTick(function() { + success.apply(overlay,results); + }); + } + + // Insert small delays only for `map.getMap()`, + // because if you execute three or four `map.getMap()` at the same time, + // Android OS itself crashes. + // In order to prevent this error, insert small delays. + var delay = 0; + if (methodName === _isWaitMethod) { + if (_isWaitMethod === 'getMap' && Date.now() - _lastGetMapExecuted < 1500) { + delay = 1500; + } + _lastGetMapExecuted = Date.now(); + _isWaitMethod = null; + } + setTimeout(function() { + _executingCnt--; + common.nextTick(_exec); + }, delay); + }, + function() { + //------------------------------- + // error callback + //------------------------------- + if (methodName === 'resizeMap') { + _isResizeMapExecuting = false; + } + if (!_stopRequested && error) { + var results = Array.prototype.slice.call(arguments, 0); + common.nextTick(function() { + error.apply(overlay,results); + }); + } + + if (methodName === _isWaitMethod) { + _isWaitMethod = null; + } + _executingCnt--; + common.nextTick(_exec); + }, + pluginName, methodName, args] + }); + + // In order to execute all methods in safe, + // the maps plugin limits the number of execution in a moment to 10. + // + // Note that Cordova-Android drops has also another internal queue, + // and the internal queue drops our statement if the app send too much. + // + // Also executing too much statements at the same time, + // it would cause many errors in native side, such as out-of-memory. + // + // In order to prevent these troubles, the maps plugin limits the number of execution is 10. + if (_isExecuting || _executingCnt >= MAX_EXECUTE_CNT || _isWaitMethod || commandQueue.length === 0) { + return; + } + common.nextTick(_exec); +} +function _exec() { + + // You probably wonder why there is this code because it's already simular code at the end of the execCmd function. + // + // Because the commandQueue might change after the last code of the execCmd. + // (And yes, it was occurred.) + // In order to block surely, block the execution again. + if (_isExecuting || _executingCnt >= MAX_EXECUTE_CNT || _isWaitMethod || commandQueue.length === 0) { + return; + } + _isExecuting = true; + + // Execute some execution requests (up to 10) from the commandQueue. + var methodName; + while (commandQueue.length > 0 && _executingCnt < MAX_EXECUTE_CNT) { + if (!_stopRequested) { + _executingCnt++; + } + + // Pick up the head one. + var commandParams = commandQueue.shift(); + methodName = commandParams.args[3]; + + // If the request is `map.refreshLayout()` and another `map.refreshLayout()` is executing, + // skip it. + // This prevents to execute multiple `map.refreshLayout()` at the same time. + if (methodName === 'resizeMap') { + if (_isResizeMapExecuting) { + _executingCnt--; + continue; + } + _isResizeMapExecuting = true; + } + + // If the `_stopRequested` flag is true, + // do not execute any statements except `remove()` or `clear()` methods. + if (_stopRequested && (!commandParams.execOptions.remove || methodName !== 'clear')) { + _executingCnt--; + continue; + } + + // Some methods have to block other execution requests, such as `map.clear()` + if (commandParams.execOptions.sync) { + _isWaitMethod = methodName; + cordova_exec.apply(this, commandParams.args); + break; + } + cordova_exec.apply(this, commandParams.args); + } + + _isExecuting = false; + +} + + +//---------------------------------------------------- +// Stop all executions if the page will be closed. +//---------------------------------------------------- +function stopExecution() { + // Request stop all tasks. + _stopRequested = true; +} +window.addEventListener('unload', stopExecution); + +module.exports = execCmd; diff --git a/node_modules/cordova-plugin-googlemaps/www/encoding.js b/node_modules/cordova-plugin-googlemaps/www/encoding.js new file mode 100644 index 0000000..aa53476 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/encoding.js @@ -0,0 +1,96 @@ +var LatLng = require('./LatLng'); +var common = require('./Common'); + +/***************************************************************************** + * geometry Encode / decode points + * http://jsfiddle.net/8nzg7tta/ + *****************************************************************************/ +//decode function +function decodePath(encoded, precision) { + precision = precision || 5; + precision = Math.pow(10, -precision); + var len = encoded.length, + index = 0, + lat = 0, + lng = 0, + array = []; + while (index < len) { + var b, shift = 0, + result = 0; + do { + b = encoded.charCodeAt(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + var dlat = ((result & 1) ? ~(result >> 1) : (result >> 1)); + lat += dlat; + shift = 0; + result = 0; + do { + b = encoded.charCodeAt(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + var dlng = ((result & 1) ? ~(result >> 1) : (result >> 1)); + lng += dlng; + array.push(new LatLng(lat * precision, lng * precision)); + } + return array; +} + +//encode functions +function encodePath(points) { + var plat = 0; + var plng = 0; + var encoded_points = ''; + + points = common.convertToPositionArray(points); + + for (var i = 0; i < points.length; ++i) { + encoded_points += encodePoint(plat, plng, points[i].lat, points[i].lng); + plat = points[i].lat; + plng = points[i].lng; + } + + return encoded_points; +} + +function encodePoint(plat, plng, lat, lng) { + var late5 = Math.round(lat * 1e5); + var plate5 = Math.round(plat * 1e5); + + var lnge5 = Math.round(lng * 1e5); + var plnge5 = Math.round(plng * 1e5); + + var dlng = lnge5 - plnge5; + var dlat = late5 - plate5; + + return encodeSignedNumber(dlat) + encodeSignedNumber(dlng); +} + +function encodeSignedNumber(num) { + var sgn_num = num << 1; + + if (num < 0) { + sgn_num = ~(sgn_num); + } + + return (encodeNumber(sgn_num)); +} + +function encodeNumber(num) { + var encodeString = ''; + + while (num >= 0x20) { + encodeString += (String.fromCharCode((0x20 | (num & 0x1f)) + 63)); + num >>= 5; + } + + encodeString += (String.fromCharCode(num + 63)); + return encodeString; +} + +module.exports = { + decodePath: decodePath, + encodePath: encodePath +}; diff --git a/node_modules/cordova-plugin-googlemaps/www/event.js b/node_modules/cordova-plugin-googlemaps/www/event.js new file mode 100644 index 0000000..1b232a2 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/event.js @@ -0,0 +1,35 @@ +module.exports = { + MAP_READY: 'map_ready', + MAP_CLICK: 'map_click', + MAP_LONG_CLICK: 'map_long_click', + POI_CLICK: 'poi_click', + MY_LOCATION_CLICK: 'my_location_click', + MY_LOCATION_BUTTON_CLICK: 'my_location_button_click', + INDOOR_BUILDING_FOCUSED: 'indoor_building_focused', + INDOOR_LEVEL_ACTIVATED: 'indoor_level_activated', + CAMERA_MOVE_START: 'camera_move_start', + CAMERA_MOVE: 'camera_move', + CAMERA_MOVE_END: 'camera_move_end', + OVERLAY_CLICK: 'overlay_click', + POLYGON_CLICK: 'polygon_click', + POLYLINE_CLICK: 'polyline_click', + CIRCLE_CLICK: 'circle_click', + GROUND_OVERLAY_CLICK: 'groundoverlay_click', + INFO_CLICK: 'info_click', + INFO_LONG_CLICK: 'info_long_click', + INFO_CLOSE: 'info_close', + INFO_OPEN: 'info_open', + MARKER_CLICK: 'marker_click', + MARKER_DRAG: 'marker_drag', + MARKER_DRAG_START: 'marker_drag_start', + MARKER_DRAG_END: 'marker_drag_end', + MAP_DRAG: 'map_drag', + MAP_DRAG_START: 'map_drag_start', + MAP_DRAG_END: 'map_drag_end', + KML_CLICK: 'kml_click', + FUSION_TABLE_CLICK: 'fusion_table_click', + PANORAMA_READY: 'panorama_ready', + PANORAMA_CAMERA_CHANGE: 'panorama_camera_change', + PANORAMA_LOCATION_CHANGE: 'panorama_location_change', + PANORAMA_CLICK: 'panorama_click' +}; diff --git a/node_modules/cordova-plugin-googlemaps/www/geomodel.js b/node_modules/cordova-plugin-googlemaps/www/geomodel.js new file mode 100644 index 0000000..5a28588 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/geomodel.js @@ -0,0 +1,139 @@ +/** +# +# Copyright 2010 Alexandre Gellibert +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +var LatLng = require('./LatLng'), + LatLngBounds = require('./LatLngBounds'), + GEOCELL_GRID_SIZE = 4, + GEOCELL_ALPHABET = '0123456789abcdef'; + +function computeBox(geocell) { + var subcell_lng_span, subcell_lat_span; + var xy, x, y; + var bbox = _createBoundingBox(90.0, 180.0, -90.0, -180.0); + while(geocell.length > 0) { + //geoChar = geocell.charAt(i); + //pos = GEOCELL_ALPHABET.indexOf(geoChar); + + subcell_lng_span = (bbox.getEast() - bbox.getWest()) / GEOCELL_GRID_SIZE; + subcell_lat_span = (bbox.getNorth() - bbox.getSouth()) / GEOCELL_GRID_SIZE; + + xy = _subdiv_xy(geocell.charAt(0)); + x = xy[0]; + y = xy[1]; + + bbox = _createBoundingBox(bbox.getSouth() + subcell_lat_span * (y + 1), + bbox.getWest() + subcell_lng_span * (x + 1), + bbox.getSouth() + subcell_lat_span * y, + bbox.getWest() + subcell_lng_span * x); + + geocell = geocell.substring(1); + } + var sw = new LatLng(bbox.getSouth(), bbox.getWest()); + var ne = new LatLng(bbox.getNorth(), bbox.getEast()); + var bounds = new LatLngBounds(sw, ne); + return bounds; +} + +function _createBoundingBox(north, east, south, west) { + var north_,south_; + + if(south > north) { + south_ = north; + north_ = south; + } else { + south_ = south; + north_ = north; + } + return { + northEast: {lat: north_, lng: east}, + southWest: {lat: south_, lng: west}, + getNorth: function() { + return this.northEast.lat; + }, + getSouth: function() { + return this.southWest.lat; + }, + getWest: function() { + return this.southWest.lng; + }, + getEast: function() { + return this.northEast.lng; + } + }; +} + + +/** + * https://code.google.com/p/geomodel/source/browse/trunk/geo/geocell.py#370 + * @param latLng + * @param resolution + * @return + */ +function getGeocell(lat, lng, resolution) { + var cell = ''; + var north = 90.0; + var south = -90.0; + var east = 180.0; + var west = -180.0; + + var subcell_lng_span, subcell_lat_span; + var x, y; + while(cell.length < resolution + 1) { + subcell_lng_span = (east - west) / GEOCELL_GRID_SIZE; + subcell_lat_span = (north - south) / GEOCELL_GRID_SIZE; + + x = Math.min(Math.floor(GEOCELL_GRID_SIZE * (lng - west) / (east - west)), GEOCELL_GRID_SIZE - 1); + y = Math.min(Math.floor(GEOCELL_GRID_SIZE * (lat - south) / (north - south)),GEOCELL_GRID_SIZE - 1); + cell += _subdiv_char(x, y); + + south += subcell_lat_span * y; + north = south + subcell_lat_span; + + west += subcell_lng_span * x; + east = west + subcell_lng_span; + } + return cell; +} + + +/* + * Returns the (x, y) of the geocell character in the 4x4 alphabet grid. + */ +function _subdiv_xy(char) { + // NOTE: This only works for grid size 4. + var charI = GEOCELL_ALPHABET.indexOf(char); + return [(charI & 4) >> 1 | (charI & 1) >> 0, + (charI & 8) >> 2 | (charI & 2) >> 1]; +} + +/** + * Returns the geocell character in the 4x4 alphabet grid at pos. (x, y). + * @return + */ +function _subdiv_char(posX, posY) { + // NOTE: This only works for grid size 4. + return GEOCELL_ALPHABET.charAt( + (posY & 2) << 2 | + (posX & 2) << 1 | + (posY & 1) << 1 | + (posX & 1) << 0); +} + +module.exports = { + computeBox: computeBox, + getGeocell: getGeocell +}; diff --git a/node_modules/cordova-plugin-googlemaps/www/js_CordovaGoogleMaps-for-android_ios.js b/node_modules/cordova-plugin-googlemaps/www/js_CordovaGoogleMaps-for-android_ios.js new file mode 100644 index 0000000..e203d46 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/js_CordovaGoogleMaps-for-android_ios.js @@ -0,0 +1,1026 @@ + +if (!window.Promise) { + window.Promise = require('./Promise'); +} +var utils = require('cordova/utils'), + common = require('./Common'), + cordova_exec = require('cordova/exec'), + BaseClass = require('./BaseClass'), + Map = require('./Map'), + StreetViewPanorama = require('./StreetViewPanorama'); + +function CordovaGoogleMaps(execCmd) { + var self = this; + BaseClass.apply(this); + + // Ignore checking for thse tags. + self.doNotTraceTags = [ + 'svg', 'p', 'pre', 'script', 'style' + ]; + + self.execCmd = execCmd; + + // random unique number + self.saltHash = Math.floor(Math.random() * Date.now()); + + // Hold map instances. + self.MAPS = {}; + self.MAP_CNT = 0; + + // Hold the DOM hierarchy graph. + self.domPositions = {}; + + // True while the putHtmlElements is working. + self.isChecking = false; + self.transforming = false; + + // True if the code requests to execute the putHtmlElements while working it. + self.checkRequested = false; + + // True if some elements are changed, such as added an element. + self.isThereAnyChange = false; + + // Indicate the native timer is stopped or not. + self.set('isSuspended', false); + + // Cache for updateMapPositionOnly + self.prevMapRects = {}; + + // Control variables for followMaps() function + self.scrollEndTimer = null; + self.transitionEndTimer = null; + self.transformTargets = {}; + self.transitionCnt = 0; + + //------------------------------------------------------------------------------ + // Using MutationObserver, observe only added/removed or style changed elements + //------------------------------------------------------------------------------ + var observer = new MutationObserver(function(mutations) { + common.nextTick(function() { + + var doTraceTree = true; + Array.prototype.slice.call(mutations, 0).forEach(function(mutation) { + + if (mutation.type === 'childList') { + // If some elements are added, check them. + if (mutation.addedNodes) { + Array.prototype.slice.call(mutation.addedNodes, 0).forEach(function(node) { + if (node.nodeType !== Node.ELEMENT_NODE) { + return; + } + self.setDomId.call(self, node); + }); + } + + // If some elements are removed from the DOM tree, remove their information. + if (mutation.removedNodes) { + Array.prototype.slice.call(mutation.removedNodes, 0).forEach(function(node) { + if (node.nodeType !== Node.ELEMENT_NODE || !node.hasAttribute('__pluginDomId')) { + return; + } + node._isRemoved = true; + self.removeDomTree.call(self, node); + }); + } + } else { + // Some attributes are changed. + // If the element has __pluginDomId, check it. + if (mutation.target.nodeType !== Node.ELEMENT_NODE) { + return; + } + if (mutation.target.hasAttribute('__pluginDomId')) { + // elemId = mutation.target.getAttribute('__pluginDomId'); + var transformCSS = common.getStyle(mutation.target, 'transform') || + common.getStyle(mutation.target, '-webkit-transform') || + common.getStyle(mutation.target, 'transition') || + common.getStyle(mutation.target, '-webkit-transition'); + if (transformCSS !== 'none') { + mutation.target.dispatchEvent(common.createEvent('transitionstart')); + + // Omit executing the putHtmlElements() at this time, + // because it is executed at `transitionend`. + doTraceTree = false; + } + } + } + }); + + if (doTraceTree) { + self.isThereAnyChange = true; + common.nextTick(self.putHtmlElements.bind(self)); + } + }); + }); + var attachObserver = function() { + observer.observe(document.body.parentElement, { + attributes : true, + childList: true, + subtree: true, + attributeFilter: ['style', 'class'] + }); + }; + self.one('start', attachObserver); + + self.on('isSuspended_changed', function(oldValue, newValue) { + if (newValue) { + cordova_exec(null, null, 'CordovaGoogleMaps', 'pause', []); + } else { + cordova_exec(null, null, 'CordovaGoogleMaps', 'resume', []); + } + }); +} + +utils.extend(CordovaGoogleMaps, BaseClass); + +CordovaGoogleMaps.prototype.traceDomTree = function(element, elemId, isMapChild) { + //------------------------------------------ + // Create the DOM hierarchy graph + //------------------------------------------ + var self = this; + + // If the root DOM element should be ignored, + // remove it from the tree graph. + if (self.doNotTraceTags.indexOf(element.tagName.toLowerCase()) > -1 || + !common.shouldWatchByNative(element)) { + self.removeDomTree.call(self, element); + return; + } + + // Get the z-index CSS + var zIndexProp = common.getZIndex(element); + + // Stores dom information + var isCached = elemId in self.domPositions; + self.domPositions[elemId] = { + + // If `pointer-events:none`, continue the hitTest process + pointerEvents: common.getStyle(element, 'pointer-events'), + + // Only true if element is mapDiv + isMap: element.hasAttribute('__pluginMapId'), + + // Calculate dom clickable region + size: common.getDivRect(element), + + // Calculate actual z-index value + zIndex: zIndexProp, + + // If `overflow: hidden or scroll`, the native side needs to recalculate actual size + overflowX: common.getStyle(element, 'overflow-x'), + overflowY: common.getStyle(element, 'overflow-y'), + + // Hold the elementId of child elements + children: [], + + // Hold the list of map id. + containMapIDs: (isCached ? self.domPositions[elemId].containMapIDs : {}) + }; + + // Should this process continue to child elements? + // - condition 1: + // If the child element contains map, should continue. + // + // - condition 2: + // If the element is one of the children of map div, + // check all elements, especially for HtmlInfoWindow. + // + // - condition 3: + // If the pointer-css is 'none', continue the proces, + // because the element might be a container of the map. + // + // - condition 4: + // If the z-index is 'inherit', there might be some elements. + var containMapCnt = (Object.keys(self.domPositions[elemId].containMapIDs)).length; + isMapChild = isMapChild || self.domPositions[elemId].isMap; + if ((containMapCnt > 0 || isMapChild || self.domPositions[elemId].pointerEvents === 'none' || zIndexProp.isInherit) && element.children.length > 0) { + Array.prototype.slice.call(element.children, 0).forEach(function functionName(child) { + if (self.doNotTraceTags.indexOf(child.tagName.toLowerCase()) > -1 || + !common.shouldWatchByNative(child)) { + self.removeDomTree.call(self, child); + return; + } + + var childId = common.getPluginDomId(child); + self.domPositions[elemId].children.push(childId); + self.traceDomTree.call(self, child, childId, isMapChild); + }); + } +}; + +CordovaGoogleMaps.prototype.setDomId = function(element) { + //---------------------------------------------------------------------- + // This procedure generates unique ID + // for all elements under the element, and the element itself. + //---------------------------------------------------------------------- + common.getPluginDomId(element); + if (element.children) { + Array.prototype.slice.call(element.children, 0).forEach(common.getPluginDomId.bind(self)); + } +}; + +CordovaGoogleMaps.prototype.putHtmlElements = function() { + //---------------------------------------------------------------------- + // This procedure generates a DOM hierarchy tree graph from . + //---------------------------------------------------------------------- + var self = this; + + // If this process is working, just checkRequested = true. + // The putHtmlElements will execute itself if the flag is true. + if (self.isChecking || self.transforming) { + self.checkRequested = true; + return; + } + self.checkRequested = false; + + // If no elements are changed after the last checking, + // stop the native timer in order to save the battery usage. + if (!self.isThereAnyChange) { + self.pause(); + return; + } + + + self.isChecking = true; + + // If there is no visible or clickable map, + // stop checking + var mapIDs = Object.keys(self.MAPS); + var touchableMapList = mapIDs.filter(function(mapId) { + var map = self.MAPS[mapId]; + var isTouchable = false; + if (map) { + var mapDiv = map.getDiv(); + isTouchable = ( + map.getVisible() && + // map.getClickable() && <-- don't consider this. + mapDiv && + common.shouldWatchByNative(mapDiv)); + if (isTouchable) { + var elemId = common.getPluginDomId(mapDiv); + var domInfo = self.domPositions[elemId]; + if (domInfo) { + self.domPositions[elemId].size = common.getDivRect(mapDiv); + isTouchable = domInfo.size.width * domInfo.size.height > 0; + } else { + isTouchable = false; + } + } + map.set('__isAttached', isTouchable); + } + return isTouchable; + }); + if (self._prevMapCnt === 0 && touchableMapList.length === 0) { + self.pause(); + return; + } + self._prevMapCnt = touchableMapList.length; + + // If there is another check request, + // DOM tree might be changed. + // So let's start again. + if (self.checkRequested || self.transforming) { + setTimeout(function() { + self.isChecking = false; + common.nextTick(self.putHtmlElements.bind(self)); + }, 50); + return; + } + + // Since the native side needs to know the 'latest' DOM information, + // clear the DOM cache. + common._clearInternalCache(); + + // Generate the DOM hierarchy tree from tag. + common.getPluginDomId(document.body); + self.traceDomTree.call(self, document.body, 'root', false); + + // If the map div is not displayed (such as display='none'), + // ignore the map temporally. + var stopFlag = false; + mapIDs.forEach(function(mapId) { + var div = self.MAPS[mapId].getDiv(); + if (!div || stopFlag) { + return; + } + + // Does this dom is really existed? + var elemId = div.getAttribute('__pluginDomId'); + if (!elemId) { + // The map div is removed + if (mapId in self.MAPS) { + self.MAPS[mapId].remove(); + } + stopFlag = true; + return; + } + + if (!(elemId in self.domPositions)) { + // Is the map div removed? + var ele = document.querySelector('[__pluginMapId="' + mapId + '"]'); + if (!ele) { + // If no div element, remove the map. + if (mapId in self.MAPS) { + self.MAPS[mapId].remove(); + } + stopFlag = true; + } + return; + } + + }); + + if (stopFlag || self.transforming) { + // There is no map information (maybe timining?), + // or the another check request is waiting, + // start again. + self.isThereAnyChange = true; + setTimeout(function() { + self.isChecking = false; + common.nextTick(self.putHtmlElements.bind(self)); + }, 50); + return; + } + + //----------------------------------------------------------------- + // Pass the DOM hierarchy tree graph to native side + //----------------------------------------------------------------- + self.resume(); + + //console.log('--->putHtmlElements to native (start)', JSON.parse(JSON.stringify(self.domPositions))); + cordova_exec(function() { + //console.log('--->putHtmlElements to native (done)'); + + // If there is another checking request, try again. + if (self.checkRequested) { + setTimeout(function() { + self.isChecking = false; + common.nextTick(self.putHtmlElements.bind(self)); + }, 50); + return; + } + setTimeout(function() { + if (!self.isChecking && !self.transforming) { + common.nextTick(self.followMapDivPositionOnly.bind(self)); + } + }, 50); + self.isChecking = false; + self.pause(); + }, null, 'CordovaGoogleMaps', 'putHtmlElements', [self.domPositions]); +}; + + +CordovaGoogleMaps.prototype.pause = function() { + var self = this; + + self.set('isSuspended', true); + self.isThereAnyChange = false; + self.isChecking = false; +}; + +CordovaGoogleMaps.prototype.resume = function() { + var self = this; + self.set('isSuspended', false); +}; + + +// If the `transitionend` event is ocurred on the observed element, +// adjust the position and size of the map view + +CordovaGoogleMaps.prototype.followMaps = function(evt) { + var self = this; + if (self.MAP_CNT === 0) { + return; + } + self.transitionCnt++; + self.transforming = true; + var changes = self.followMapDivPositionOnly.call(self); + if (self.scrollEndTimer) { + clearTimeout(self.scrollEndTimer); + self.scrollEndTimer = null; + } + if (changes) { + self.scrollEndTimer = setTimeout(self.followMaps.bind(self, evt), 100); + } else { + setTimeout(self.onTransitionEnd.bind(self, evt), 100); + } +}; + +// CSS event `transitionend` is fired even the target dom element is still moving. +// In order to detect 'correct demention after the transform', wait until stable. +CordovaGoogleMaps.prototype.onTransitionEnd = function(evt) { + var self = this; + if (self.MAP_CNT === 0 || !evt) { + return; + } + var target = evt.target; + if (!target) { + target = document.body; + } + target = target.getAttribute === 'function' ? target : document.body; + var elemId = target.getAttribute('__pluginDomId'); + self.transformTargets[elemId] = {left: -1, top: -1, right: -1, bottom: -1, finish: false, target: target}; + if (!self.transitionEndTimer) { + self.transitionEndTimer = setTimeout(self.detectTransitionFinish.bind(self), 100); + } +}; + +CordovaGoogleMaps.prototype.detectTransitionFinish = function() { + var self = this; + var keys = Object.keys(self.transformTargets); + var onFilter = function(elemId) { + if (self.transformTargets[elemId].finish) { + return false; + } + + var target = self.transformTargets[elemId].target; + var divRect = common.getDivRect(target); + var prevRect = self.transformTargets[elemId]; + if (!prevRect || + divRect.left === prevRect.left && + divRect.top === prevRect.top && + divRect.right === prevRect.right && + divRect.bottom === prevRect.bottom) { + self.transformTargets[elemId].finish = true; + } + self.transformTargets[elemId].left = divRect.left; + self.transformTargets[elemId].top = divRect.top; + self.transformTargets[elemId].right = divRect.right; + self.transformTargets[elemId].bottom = divRect.bottom; + return !self.transformTargets[elemId].finish; + }; + var notYetTargets = keys.filter(onFilter); + onFilter = null; + + if (self.transitionEndTimer) { + clearTimeout(self.transitionEndTimer); + } + self.transitionCnt -= notYetTargets.length > 1 ? 1 : 0; + if (notYetTargets.length > 0 && self.transitionCnt == 0) { + self.transitionEndTimer = setTimeout(self.detectTransitionFinish.bind(self), 100); + } else { + clearTimeout(self.transitionEndTimer); + self.transitionEndTimer = null; + setTimeout(self.onTransitionFinish.bind(self), 100); + } +}; + +CordovaGoogleMaps.prototype.onTransitionFinish = function() { + var self = this; + if (self.MAP_CNT === 0) { + self.transforming = false; + return; + } + // Don't block by transform flag + // because some ionic CSS technique can not trigger `transitionstart` event. + // if (!self.transforming) { + // return; + // } + self.transforming = false; + var changes = self.followMapDivPositionOnly.call(self); + if (changes) { + self.scrollEndTimer = setTimeout(self.onTransitionFinish.bind(self), 100); + } else { + self.transformTargets = undefined; + self.transformTargets = {}; + self.isThereAnyChange = true; + self.checkRequested = false; + self.putHtmlElements.call(self); + //self.pause(); + self.scrollEndTimer = null; + } +}; + + +CordovaGoogleMaps.prototype.removeDomTree = function(node) { + //---------------------------------------------------------------------------- + // This procedure removes the DOM tree graph from the specified element(node) + //---------------------------------------------------------------------------- + var self = this; + + if (!node || !node.querySelectorAll) { + return; + } + + // Remove the information of children + // which have the `__pluginDomId` attribute. + var nodeList = node.querySelectorAll('[__pluginDomId]'); + var children = []; + for (var i = 0; i < nodeList.length; i++) { + children.push(nodeList[i]); + } + children.push(node); + + var isRemoved = node._isRemoved; + children.forEach(function(child) { + var elemId = child.getAttribute('__pluginDomId'); + + // If the DOM is removed from the DOM tree, + // remove the attribute. + // (Note that the `_isRemoved` flag is set in MutationObserver.) + if (isRemoved) { + child.removeAttribute('__pluginDomId'); + + // If map div, remove the map also. + if (child.hasAttribute('__pluginMapId')) { + var mapId = child.getAttribute('__pluginMapId'); + if (mapId in self.MAPS) { + self.MAPS[mapId].remove(); + } + } + delete self.domPositions[elemId]; + } + common._removeCacheById(elemId); + }); + +}; + +CordovaGoogleMaps.prototype.invalidate = function(opts) { + //------------------------------- + // Recheck the DOM positions + //------------------------------- + var self = this; + + opts = opts || {}; + if (opts.force) { + self.isThereAnyChange = true; + } + self.followMapDivPositionOnly.call(self, opts); + + common.nextTick(function() { + self.resume.call(self); + self.putHtmlElements.call(self); + }); +}; + +CordovaGoogleMaps.prototype.followMapDivPositionOnly = function(opts) { + //---------------------------------------------------------------------------- + // Follow the map div position and size only without the DOM check. + // This is designed for scrolling. + //---------------------------------------------------------------------------- + var self = this; + + opts = opts || {}; + var mapRects = {}; + var mapIDs = Object.keys(self.MAPS); + var changed = false; + mapIDs.forEach(function(mapId) { + var map = self.MAPS[mapId]; + + if (map && + map.getVisible() && + map.getDiv() && + common.shouldWatchByNative(map.getDiv())) { + + // Obtain only minimum information + var mapDiv = map.getDiv(); + var divId = mapDiv.getAttribute('__pluginDomId'); + mapRects[divId] = { + size: common.getDivRect(mapDiv), + zIndex: common.getZIndex(mapDiv) + }; + + // Is the map moved? + if (!changed && self.prevMapRects && divId in self.prevMapRects) { + if (self.prevMapRects[divId].size.left !== mapRects[divId].size.left) { + //console.log('changed(left)', divId, self.prevMapRects[divId].size.left, mapRects[divId].size.left); + changed = true; + } + if (!changed && self.prevMapRects[divId].size.top !== mapRects[divId].size.top) { + //console.log('changed(top)', divId, self.prevMapRects[divId].size.top, mapRects[divId].size.top); + changed = true; + } + if (!changed && self.prevMapRects[divId].size.width !== mapRects[divId].size.width) { + //console.log('changed(width)', divId, self.prevMapRects[divId].size.width, mapRects[divId].size.width); + changed = true; + } + if (!changed && self.prevMapRects[divId].size.height !== mapRects[divId].size.height) { + //console.log('changed(height)', divId, self.prevMapRects[divId].size.height, mapRects[divId].size.height); + changed = true; + } + if (!changed && self.prevMapRects[divId].zIndex.z !== mapRects[divId].zIndex.z) { + //console.log('changed(zIndex.z)', divId, self.prevMapRects[divId].zIndex.z, mapRects[divId].zIndex.z); + changed = true; + } + } + } + + }); + self.prevMapRects = mapRects; + + // If changed, move the map views. + if (changed || opts.force) { + cordova_exec(null, null, 'CordovaGoogleMaps', 'updateMapPositionOnly', [mapRects]); + return changed; + } + return false; +}; + +CordovaGoogleMaps.prototype.invalidateN = function(cnt) { + var self = this; + if (self.cnt > 0) { + return; + } + self.cnt = cnt; + var timer = setInterval(function() { + common.nextTick(function() { + self.followMapDivPositionOnly.call(self); + self.cnt--; + if (self.cnt === 0) { + clearInterval(timer); + self.invalidate.call(self, {force: true}); + } + }); + }, 50); +}; + + +CordovaGoogleMaps.prototype.getMap = function(div, mapOptions) { + + //---------------------------------------------------------------------------- + // This procedure return a map instance. + // - usage 1 + // plugin.google.maps.Map.getMap(options?) returns a map instance. + // + // - usage 2 + // plugin.google.maps.Map.getMap(mapDiv, options?) returns a map instance. + // The generated map follows the mapDiv position and size automatically. + // + // - usage 3 (not good way) + // In order to keep the backward compatibility for v1, + // if the mapDiv has already a map, returns the map instance for the map div. + //---------------------------------------------------------------------------- + var self = this, + mapId, elem, elemId; + + if (common.isDom(div)) { + mapId = div.getAttribute('__pluginMapId'); + + // Wow, the app specifies the map div that has already another map, + // but the app try to create new map. + // In this case, remove the old map instance automatically. + if (mapId && self.MAPS[mapId].getDiv() !== div) { + elem = self.MAPS[mapId].getDiv(); + while(elem && elem.nodeType === Node.ELEMENT_NODE) { + elemId = elem.getAttribute('__pluginDomId'); + if (elemId && elemId in self.domPositions) { + self.domPositions[elemId].containMapIDs = self.domPositions[elemId].containMapIDs || {}; + delete self.domPositions[elemId].containMapIDs[mapId]; + if ((Object.keys(self.domPositions[elemId].containMapIDs).length) < 1) { + delete self.domPositions[elemId]; + } + } + elem = elem.parentNode; + } + self.MAPS[mapId].remove(); + mapId = undefined; + } + + if (mapId && mapId in self.MAPS) { + // Usage 3 + // If the map div has already a map, + // return the map instance. + return self.MAPS[mapId]; + } + + } + if (!mapId) { + mapId = 'map_' + self.MAP_CNT + '_' + self.saltHash; + } + // Create a map instance. + var map = new Map(mapId, self.execCmd); + map.set('__isAttached', common.isDom(div)); + + // Catch all events for this map instance, then pass to the instance. + // (Don't execute this native callback from your code) + window.plugin.google.maps[mapId] = nativeCallback.bind(map); + + map.on('__isAttached_changed', function(oldValue, newValue) { + if (newValue) { + cordova_exec(null, null, map.__pgmId, 'attachToWebView', []); + } else { + cordova_exec(null, null, map.__pgmId, 'detachFromWebView', []); + } + }); + + // If the mapDiv is changed, clean up the information for old map div, + // then add new information for new map div. + map.on('div_changed', function(oldDiv, newDiv) { + var elemId, ele; + + if (common.isDom(oldDiv)) { + oldDiv.removeAttribute('__pluginMapId'); + ele = oldDiv; + while(ele && ele != document.body.parentNode) { + elemId = ele.getAttribute('__pluginDomId'); + if (elemId) { + self.domPositions[elemId].containMapIDs = self.domPositions[elemId].containMapIDs || {}; + delete self.domPositions[elemId].containMapIDs[mapId]; + if ((Object.keys(self.domPositions[elemId].containMapIDs)).length < 1) { + delete self.domPositions[elemId]; + } + } + ele.removeAttribute('__pluginDomId'); + if (ele.classList) { + ele.classList.remove('_gmaps_cdv_'); + } else if (ele.className) { + ele.className = ele.className.replace(/_gmaps_cdv_/g, ''); + ele.className = ele.className.replace(/\s+/g, ' '); + } + ele = ele.parentNode; + } + } + + if (common.isDom(newDiv)) { + + elemId = common.getPluginDomId(newDiv); + + elem = newDiv; + var isCached; + while(elem && elem.nodeType === Node.ELEMENT_NODE) { + elemId = common.getPluginDomId(elem); + if (common.shouldWatchByNative(elem)) { + if (elem.shadowRoot) { + elem.shadowRoot.addEventListener('transitionend', self.onTransitionEnd.bind(self), {capture: true}); + elem.shadowRoot.addEventListener('scroll', self.followMaps.bind(self), {capture: true}); + } + isCached = elemId in self.domPositions; + self.domPositions[elemId] = { + pointerEvents: common.getStyle(elem, 'pointer-events'), + isMap: false, + size: common.getDivRect(elem), + zIndex: common.getZIndex(elem), + children: (elemId in self.domPositions ? self.domPositions[elemId].children : []), + overflowX: common.getStyle(elem, 'overflow-x'), + overflowY: common.getStyle(elem, 'overflow-y'), + containMapIDs: (isCached ? self.domPositions[elemId].containMapIDs : {}) + }; + self.domPositions[elemId].containMapIDs[mapId] = 1; + } else { + self.removeDomTree.call(self, elem); + } + elem = elem.parentNode; + } + + elemId = common.getPluginDomId(newDiv); + self.domPositions[elemId].isMap = true; + } + }); + + // If the map is removed, clean up the information. + map.one('remove', self._remove.bind(self, mapId)); + self.MAP_CNT++; + self.isThereAnyChange = true; + + if (div instanceof Promise) { + // This hack code for @ionic-native/google-maps + div.then(function(params) { + self.MAPS[mapId] = map; + params = params || []; + params.unshift(map); + postMapInit.apply(self, params); + }); + } else { + // Normal code flow + self.MAPS[mapId] = map; + postMapInit.call(self, map, div, mapOptions); + } + + return map; +}; + +CordovaGoogleMaps.prototype.getPanorama = function(div, streetViewOptions) { + var self = this; + var mapId = 'streetview_' + self.MAP_CNT + '_' + self.saltHash; + + // Create a panorama instance. + var panorama = new StreetViewPanorama(mapId, self.execCmd); + + // Catch all events for this map instance, then pass to the instance. + // (Don't execute this native callback from your code) + window.plugin.google.maps[mapId] = nativeCallback.bind(panorama); + + self.MAP_CNT++; + + panorama.one('remove', self._remove.bind(self, mapId)); + + if (div instanceof Promise) { + // This hack code for @ionic-native/google-maps + div.then(function(params) { + self.MAPS[mapId] = panorama; + params = params || []; + params.unshift(panorama); + postPanoramaInit.apply(self, params); + }); + } else { + // Normal code flow + self.MAPS[mapId] = panorama; + postPanoramaInit.call(self, panorama, div, streetViewOptions); + } + + return panorama; +}; + +CordovaGoogleMaps.prototype._remove = function(mapId) { + var self = this; + delete window.plugin.google.maps[mapId]; + var map = self.MAPS[mapId]; + if (map) { + var div = map.getDiv(); + if (!div) { + div = document.querySelector('[__pluginMapId="' + mapId + '"]'); + } + if (div) { + div.removeAttribute('__pluginMapId'); + } + + var keys = Object.keys(self.domPositions); + keys.forEach(function(elemId) { + self.domPositions[elemId].containMapIDs = self.domPositions[elemId].containMapIDs || {}; + delete self.domPositions[elemId].containMapIDs[mapId]; + if ((Object.keys(self.domPositions[elemId].containMapIDs)).length < 1) { + delete self.domPositions[elemId]; + } + }); + self.MAPS[mapId].destroy(); + } + + delete self.MAPS[mapId]; + map = undefined; + + // If the app have no map, stop the native timer. + if ((Object.keys(self.MAPS)).length === 0) { + common._clearInternalCache(); + self.pause(); + } +}; + +function nativeCallback(params) { + var args = params.args || []; + args.unshift(params.evtName); + this[params.callback].apply(this, args); +} +function postPanoramaInit(panorama, div, options) { + var self = this; + var mapId = panorama.getId(); + self.isThereAnyChange = true; + + + if (!common.isDom(div)) { + console.error('[GoogleMaps] You need to specify a dom element(such as
) for this method', div); + return; + } + // If the given div is not fully ready, wait a little + if (!common.shouldWatchByNative(div)) { + setTimeout(function() { + common.nextTick(postPanoramaInit.bind(self, panorama, div, options)); + }, 50); + return; + } + if (div.offsetWidth < 100 || div.offsetHeight < 100) { + console.error('[GoogleMaps] Minimum container dimention is 100x100 in pixels.', div); + return; + } + + // If the mapDiv is specified, + // the native side needs to know the map div position + // before creating the map view. + div.setAttribute('__pluginMapId', mapId); + var elemId = common.getPluginDomId(div); + + var elem = div; + var isCached, zIndexList = []; + while(elem && elem.nodeType === Node.ELEMENT_NODE) { + elemId = common.getPluginDomId(elem); + if (common.shouldWatchByNative(elem)) { + isCached = elemId in self.domPositions; + self.domPositions[elemId] = { + pointerEvents: common.getStyle(elem, 'pointer-events'), + isMap: false, + size: common.getDivRect(elem), + zIndex: common.getZIndex(elem), + children: [], + overflowX: common.getStyle(elem, 'overflow-x'), + overflowY: common.getStyle(elem, 'overflow-y'), + containMapIDs: (isCached ? self.domPositions[elemId].containMapIDs : {}) + }; + zIndexList.unshift(self.domPositions[elemId].zIndex); + self.domPositions[elemId].containMapIDs[mapId] = 1; + } else { + self.removeDomTree.call(self, elem); + } + elem = elem.parentNode; + } + + // Calculate the native view z-index + var depth = 0; + zIndexList.forEach(function(info, idx) { + if (!info.isInherit && info.z === 0) { + depth *= 10; + } + depth += (info.z + 1) / (1 << idx) + 0.01; + }); + depth = Math.floor(depth * 10000); + + elemId = common.getPluginDomId(div); + self.domPositions[elemId].isMap = true; + + var args = Array.prototype.slice.call(arguments, 0); + args.unshift({ + __pgmId: mapId, + depth: depth + }); + + cordova_exec(function() { + panorama.getPanorama.apply(panorama, args); + }, null, 'CordovaGoogleMaps', 'putHtmlElements', [self.domPositions]); + + +} + +function postMapInit(map, div, options) { + var self = this; + var mapId = map.getId(); + var args = []; + + if (common.isDom(div)) { + // If the given div is not fully ready, wait a little + if (!common.shouldWatchByNative(div)) { + setTimeout(function() { + common.nextTick(postMapInit.bind(self, map, div, options)); + }, 50); + return; + } + if (div.offsetWidth < 100 || div.offsetHeight < 100) { + console.error('[GoogleMaps] Minimum container dimention is 100x100 in pixels.', div); + return; + } + // If the mapDiv is specified, + // the native side needs to know the map div position + // before creating the map view. + div.setAttribute('__pluginMapId', mapId); + var elemId = common.getPluginDomId(div); + + var elem = div; + var isCached; + var zIndexList = []; + while(elem && elem.nodeType === Node.ELEMENT_NODE) { + elemId = common.getPluginDomId(elem); + if (common.shouldWatchByNative(elem)) { + isCached = elemId in self.domPositions; + self.domPositions[elemId] = { + pointerEvents: common.getStyle(elem, 'pointer-events'), + isMap: false, + size: common.getDivRect(elem), + zIndex: common.getZIndex(elem), + children: [], + overflowX: common.getStyle(elem, 'overflow-x'), + overflowY: common.getStyle(elem, 'overflow-y'), + containMapIDs: (isCached ? self.domPositions[elemId].containMapIDs : {}) + }; + zIndexList.unshift(self.domPositions[elemId].zIndex); + self.domPositions[elemId].containMapIDs[mapId] = 1; + } else { + self.removeDomTree.call(self, elem); + } + elem = elem.parentNode; + } + + // Calculate the native view z-index + var depth = 0; + zIndexList.forEach(function(info, idx) { + if (!info.isInherit && info.z === 0) { + depth *= 10; + } + depth += (info.z + 1) / (1 << idx) + 0.01; + }); + depth = Math.floor(depth * 10000); + args.push({ + __pgmId: mapId, + depth: depth + }); + args.push(div); + if (options) { + args.push(options); + } + + elemId = common.getPluginDomId(div); + self.domPositions[elemId].isMap = true; + + cordova_exec(function() { + map.getMap.apply(map, args); + }, null, 'CordovaGoogleMaps', 'putHtmlElements', [self.domPositions]); + } else { + args.push({ + __pgmId: mapId, + depth: 0 + }); + args.push(null); + if (options) { + args.push(options); + } + map.getMap.apply(map, args); + } +} + + +module.exports = CordovaGoogleMaps; diff --git a/node_modules/cordova-plugin-googlemaps/www/js_CordovaGoogleMaps-for-browser.js b/node_modules/cordova-plugin-googlemaps/www/js_CordovaGoogleMaps-for-browser.js new file mode 100644 index 0000000..bf4d83b --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/js_CordovaGoogleMaps-for-browser.js @@ -0,0 +1,282 @@ + + +if (!window.Promise) { + window.Promise = require('cordova-plugin-googlemaps.Promise'); +} + +var common = require('cordova-plugin-googlemaps.Common'), + Map = require('cordova-plugin-googlemaps.Map'), + StreetViewPanorama = require('cordova-plugin-googlemaps.StreetViewPanorama'); + + +function nativeCallback(params) { + var args = params.args || []; + args.unshift(params.evtName); + + this[params.callback].apply(this, args); +} + +function CordovaGoogleMaps(execCmd) { + var self = this; + + self.execCmd = execCmd; + + // random unique number + self.saltHash = Math.floor(Math.random() * Date.now()); + + // Hold map instances. + self.MAPS = {}; + self.MAP_CNT = 0; + + var removeMapDiv = function(node) { + if (node.hasAttribute('__pluginmapid') && !node.parentNode) { + var mapId = node.getAttribute('__pluginmapid'); + var map = self.MAPS[mapId]; + if (map) { + map.remove(); + delete self.MAPS[mapId]; + } + } else { + var childNodes = Array.prototype.slice.call(node.childNodes); + childNodes.forEach(function(child) { + if (child.outerHTML && child.outerHTML.indexOf('__pluginmapid') > -1) { + removeMapDiv(child); + } + }); + } + }; + + //------------------------------------------------------------------------------ + // Using MutationObserver, observe only added/removed or style changed elements + //------------------------------------------------------------------------------ + var observer = new MutationObserver(function(mutations) { + common.nextTick(function() { + var mutationRecords = Array.prototype.slice.call(mutations, 0); + mutationRecords.forEach(function(record) { + if (record.removedNodes.length > 0) { + record.removeNodes = Array.prototype.slice.call(record.removedNodes, 0); + record.removeNodes.forEach(function(node) { + if (node.outerHTML && node.outerHTML.indexOf('__pluginmapid') > -1) { + removeMapDiv(node); + } + }); + } + }); + }); + }); + observer.observe(document.body.parentElement, { + childList: true, + subtree: true + }); +} + +CordovaGoogleMaps.prototype.getMap = function(div, mapOptions) { + + //---------------------------------------------------------------------------- + // This procedure return a map instance. + // - usage 1 + // plugin.google.maps.Map.getMap(options?) returns a map instance. + // + // - usage 2 + // plugin.google.maps.Map.getMap(mapDiv, options?) returns a map instance. + // The generated map follows the mapDiv position and size automatically. + // + // - usage 3 (not good way) + // In order to keep the backward compatibility for v1, + // if the mapDiv has already a map, returns the map instance for the map div. + //---------------------------------------------------------------------------- + var self = this, + mapId; + + if (common.isDom(div)) { + mapId = div.getAttribute('__pluginMapId'); + + // Wow, the app specifies the map div that has already another map, + // but the app try to create new map. + // In this case, remove the old map instance automatically. + if (mapId && self.MAPS[mapId].getDiv() !== div) { + var oldMap = self.MAPS[mapId]; + if (oldMap.infoWindowLayer) { + var oldDiv = oldMap.getDiv(); + oldDiv.removeChild(oldMap.infoWindowLayer); + } + oldMap.remove(); + oldMap = undefined; + mapId = undefined; + } + + if (mapId && mapId in self.MAPS) { + // Usage 3 + // If the map div has already a map, + // return the map instance. + return self.MAPS[mapId]; + } + + } + if (!mapId) { + mapId = 'map_' + self.MAP_CNT + '_' + self.saltHash; + } + // Create a map instance. + var map = new Map(mapId, self.execCmd); + window.plugin.google.maps[mapId] = nativeCallback.bind(map); + + // If the map is removed, clean up the information. + map.one('remove', self._remove.bind(self, mapId)); + self.MAP_CNT++; + self.isThereAnyChange = true; + + if (div instanceof Promise) { + // This hack code for @ionic-native/google-maps + div.then(function(params) { + self.MAPS[mapId] = map; + params = params || []; + params.unshift(map); + postMapInit.apply(self, params); + }); + } else { + // Normal code flow + self.MAPS[mapId] = map; + postMapInit.call(self, map, div, mapOptions); + } + + return map; +}; + +CordovaGoogleMaps.prototype.getPanorama = function(div, streetViewOptions) { + var self = this; + var mapId = 'streetview_' + self.MAP_CNT + '_' + self.saltHash; + + // Create a panorama instance. + var panorama = new StreetViewPanorama(mapId, self.execCmd); + + // Catch all events for this map instance, then pass to the instance. + // (Don't execute this native callback from your code) + window.plugin.google.maps[mapId] = nativeCallback.bind(panorama); + + self.MAP_CNT++; + panorama.one('remove', self._remove.bind(self, mapId)); + if (div instanceof Promise) { + // This hack code for @ionic-native/google-maps + div.then(function(params) { + self.MAPS[mapId] = panorama; + params = params || []; + params.unshift(panorama); + postPanoramaInit.apply(self, params); + }); + } else { + // Normal code flow + self.MAPS[mapId] = panorama; + postPanoramaInit.call(self, panorama, div, streetViewOptions); + } + + return panorama; +}; + +CordovaGoogleMaps.prototype._remove = function(mapId) { + var self = this; + var map = self.MAPS[mapId]; + if (map) { + + var div = map.getDiv(); + if (!div) { + div = document.querySelector('[__pluginMapId="' + mapId + '"]'); + } + if (div) { + div.removeAttribute('__pluginMapId'); + } + + self.MAPS[mapId].destroy(); + } + delete self.MAPS[mapId]; + map = undefined; +}; + +function postPanoramaInit(panorama, div, options) { + var self = this; + var mapId = panorama.getId(); + self.isThereAnyChange = true; + + if (!common.isDom(div)) { + console.error('[GoogleMaps] You need to specify a dom element(such as
) for this method', div); + return; + } + // If the given div is not fully ready, wait a little + if (!common.shouldWatchByNative(div)) { + setTimeout(function() { + common.nextTick(postPanoramaInit.bind(self, panorama, div, options)); + }, 50); + return; + } + if (div.offsetWidth < 100 || div.offsetHeight < 100) { + console.error('[GoogleMaps] Minimum container dimention is 100x100 in pixels.', div); + return; + } + + // If the mapDiv is specified, + // the native side needs to know the map div position + // before creating the map view. + div.setAttribute('__pluginMapId', mapId); + + if (div.offsetWidth < 100 || div.offsetHeight < 100) { + console.error('[GoogleMaps] Minimum container dimention is 100x100 in pixels.', div); + return; + } + var args = Array.prototype.slice.call(arguments, 0); + args.unshift({ + __pgmId: mapId + }); + + // If the mapDiv is specified, + // the native side needs to know the map div position + // before creating the map view. + div.setAttribute('__pluginMapId', mapId); + + panorama.getPanorama.apply(panorama, args); +} + +function postMapInit(map, div, options) { + var self = this; + var mapId = map.getId(); + var args = []; + + if (common.isDom(div)) { + // If the given div is not fully ready, wait a little + if (!common.shouldWatchByNative(div)) { + setTimeout(function() { + common.nextTick(postMapInit.bind(self, map, div, options)); + }, 50); + return; + } + if (div.offsetWidth < 100 || div.offsetHeight < 100) { + console.error('[GoogleMaps] Minimum container dimention is 100x100 in pixels.', div); + return; + } + // If the mapDiv is specified, + // the native side needs to know the map div position + // before creating the map view. + div.setAttribute('__pluginMapId', mapId); + + args.push({ + __pgmId: mapId, + depth: 0 + }); + args.push(div); + if (options) { + args.push(options); + } + map.getMap.apply(map, args); + } else { + args.push({ + __pgmId: mapId, + depth: 0 + }); + args.push(null); + if (options) { + args.push(options); + } + map.getMap.apply(map, args); + } +} + + +module.exports = CordovaGoogleMaps; diff --git a/node_modules/cordova-plugin-googlemaps/www/plugin-loader-for-android_ios.js b/node_modules/cordova-plugin-googlemaps/www/plugin-loader-for-android_ios.js new file mode 100644 index 0000000..593e64c --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/plugin-loader-for-android_ios.js @@ -0,0 +1,163 @@ +if (!cordova) { + document.addEventListener('deviceready', function () { + require('cordova/exec')(null, null, 'CordovaGoogleMaps', 'pause', []); + }, { + once: true + }); +} else { + var common = require('./Common'); + + cordova.addConstructor(function () { + if (!window.Cordova) { + window.Cordova = cordova; + } + window.plugin = window.plugin || {}; + window.plugin.google = window.plugin.google || {}; + window.plugin.google.maps = window.plugin.google.maps || module.exports; + + document.addEventListener('deviceready', function () { + // workaround for issue on android-19: Cannot read property 'maps' of undefined + if (!window.plugin) { + console.warn('re-init window.plugin'); + window.plugin = window.plugin || {}; + } + if (!window.plugin.google) { + console.warn('re-init window.plugin.google'); + window.plugin.google = window.plugin.google || {}; + } + if (!window.plugin.google.maps) { + console.warn('re-init window.plugin.google.maps'); + window.plugin.google.maps = window.plugin.google.maps || module.exports; + } + + cordova.exec(null, function (message) { + alert(message); + }, 'PluginEnvironment', 'isAvailable', ['']); + }, { + once: true + }); + }); + + var execCmd = require('./commandQueueExecutor'); + var cordovaGoogleMaps = new(require('./js_CordovaGoogleMaps'))(execCmd); + + (new Promise(function (resolve) { + var wait = function () { + if (document.body) { + wait = undefined; + cordovaGoogleMaps.trigger('start'); + resolve(); + } else { + setTimeout(wait, 50); + } + }; + wait(); + + })).then(function () { + // The pluginInit.js must execute before loading HTML is completed. + require('./pluginInit')(); + + common.nextTick(function () { + // If the developer needs to recalculate the DOM tree graph, + // use `cordova.fireDocumentEvent('plugin_touch')` + document.addEventListener('plugin_touch', cordovaGoogleMaps.invalidate.bind(cordovaGoogleMaps)); + + // Repositioning 30 times when the device orientaion is changed. + window.addEventListener('orientationchange', cordovaGoogleMaps.followMaps.bind(cordovaGoogleMaps, { + target: document.body + })); + + // If is not ready yet, wait 25ms, then execute this function again. + // if (!document.body || !document.body.firstChild) { + // common.nextTick(arguments.callee, 25); + // return; + // } + + document.addEventListener('transitionstart', cordovaGoogleMaps.followMaps.bind(cordovaGoogleMaps), { + capture: true + }); + document.body.parentNode.addEventListener('transitionend', cordovaGoogleMaps.onTransitionEnd.bind(cordovaGoogleMaps), { + capture: true + }); + // document.body.addEventListener('transitionend', function(e) { + // if (!e.target.hasAttribute('__pluginDomId')) { + // return; + // } + // cordovaGoogleMaps.invalidateN(5); + // }, true); + + // If the `scroll` event is ocurred on the observed element, + // adjust the position and size of the map view + document.body.parentNode.addEventListener('scroll', cordovaGoogleMaps.followMaps.bind(cordovaGoogleMaps), true); + window.addEventListener('resize', function () { + cordovaGoogleMaps.transforming = true; + cordovaGoogleMaps.onTransitionFinish.call(cordovaGoogleMaps); + }, true); + + }); + }); + + /***************************************************************************** + * Name space + *****************************************************************************/ + /** @namespace plugin.google.maps */ + + module.exports = { + event: require('./event'), + Animation: { + BOUNCE: 'BOUNCE', + DROP: 'DROP' + }, + + BaseClass: require('./BaseClass'), + BaseArrayClass: require('./BaseArrayClass'), + + /** @namespace plugin.google.maps.Map */ + Map: { + /** + * @function getMap + * @memberof plugin.google.maps.Map + * @static + */ + getMap: cordovaGoogleMaps.getMap.bind(cordovaGoogleMaps) + }, + /** @namespace plugin.google.maps.StreetView */ + StreetView: { + /** + * @function getPanorama + * @memberof plugin.google.maps.StreetView + * @static + */ + getPanorama: cordovaGoogleMaps.getPanorama.bind(cordovaGoogleMaps), + /** + * @readonly + * @enum {string} + * @memberof plugin.google.maps.StreetView + * @static + */ + Source: { + /** + * Search panorama inside and outdoor + */ + DEFAULT: 'DEFAULT', + + /** + * Search panorama inside and outdoor + */ + OUTDOOR: 'OUTDOOR' + } + }, + HtmlInfoWindow: require('./HtmlInfoWindow'), + LatLng: require('./LatLng'), + LatLngBounds: require('./LatLngBounds'), + MapTypeId: require('./MapTypeId'), + environment: require('./Environment'), + Geocoder: require('./Geocoder')(execCmd), + LocationService: require('./LocationService')(execCmd), + geometry: { + encoding: require('./encoding'), + spherical: require('./spherical'), + poly: require('./poly') + } + }; +} diff --git a/node_modules/cordova-plugin-googlemaps/www/plugin-loader-for-browser.js b/node_modules/cordova-plugin-googlemaps/www/plugin-loader-for-browser.js new file mode 100644 index 0000000..87d636a --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/plugin-loader-for-browser.js @@ -0,0 +1,46 @@ +var event = require('cordova-plugin-googlemaps.event'), + BaseClass = require('cordova-plugin-googlemaps.BaseClass'), + BaseArrayClass = require('cordova-plugin-googlemaps.BaseArrayClass'), + execCmd = require('cordova-plugin-googlemaps.commandQueueExecutor'), + cordovaGoogleMaps = new(require('cordova-plugin-googlemaps.js_CordovaGoogleMaps'))(execCmd); + +module.exports = { + event: event, + Animation: { + BOUNCE: 'BOUNCE', + DROP: 'DROP' + }, + BaseClass: BaseClass, + BaseArrayClass: BaseArrayClass, + Map: { + getMap: cordovaGoogleMaps.getMap.bind(cordovaGoogleMaps) + }, + StreetView: { + getPanorama: cordovaGoogleMaps.getPanorama.bind(cordovaGoogleMaps), + Source: { + DEFAULT: 'DEFAULT', + OUTDOOR: 'OUTDOOR' + } + }, + HtmlInfoWindow: require('cordova-plugin-googlemaps.HtmlInfoWindow'), + LatLng: require('cordova-plugin-googlemaps.LatLng'), + LatLngBounds: require('cordova-plugin-googlemaps.LatLngBounds'), + MapTypeId: require('cordova-plugin-googlemaps.MapTypeId'), + environment: require('cordova-plugin-googlemaps.Environment'), + Geocoder: require('cordova-plugin-googlemaps.Geocoder')(execCmd), + LocationService: require('cordova-plugin-googlemaps.LocationService')(execCmd), + geometry: { + encoding: require('cordova-plugin-googlemaps.encoding'), + spherical: require('cordova-plugin-googlemaps.spherical'), + poly: require('cordova-plugin-googlemaps.poly') + } +}; + +cordova.addConstructor(function () { + if (!window.Cordova) { + window.Cordova = cordova; + } + window.plugin = window.plugin || {}; + window.plugin.google = window.plugin.google || {}; + window.plugin.google.maps = window.plugin.google.maps || module.exports; +}); diff --git a/node_modules/cordova-plugin-googlemaps/www/pluginInit.js b/node_modules/cordova-plugin-googlemaps/www/pluginInit.js new file mode 100644 index 0000000..de6fea1 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/pluginInit.js @@ -0,0 +1,169 @@ +var cordova_exec = require('cordova/exec'), + common = require('cordova-plugin-googlemaps.Common'); + +function pluginInit() { + //------------------------------------------------------------- + // In some older browsers do not implement these methods. + // For example, Android 4.4 does not have Array.map() + // + // But this plugin needs them. + // That's why if the browser does not have it, implement it. + //------------------------------------------------------------- + if (typeof Array.prototype.forEach !== 'function') { + (function () { + Array.prototype.forEach = function (fn, thisArg) { + thisArg = thisArg || this; + for (var i = 0; i < this.length; i++) { + fn.call(thisArg, this[i], i, this); + } + }; + })(); + } + if (typeof Array.prototype.filter !== 'function') { + (function () { + Array.prototype.filter = function (fn, thisArg) { + thisArg = thisArg || this; + var results = []; + for (var i = 0; i < this.length; i++) { + if (fn.call(thisArg, this[i], i, this) === true) { + results.push(this[i]); + } + } + return results; + }; + })(); + } + if (typeof Array.prototype.map !== 'function') { + (function () { + Array.prototype.map = function (fn, thisArg) { + thisArg = thisArg || this; + var results = []; + for (var i = 0; i < this.length; i++) { + results.push(fn.call(thisArg, this[i], i, this)); + } + return results; + }; + })(); + } + /***************************************************************************** + * To prevent strange things happen, + * disable the changing of viewport zoom level by double clicking. + * This code has to run before the device ready event. + *****************************************************************************/ + var viewportTag = null; + var metaTags = document.getElementsByTagName('meta'); + for (var i = 0; i < metaTags.length; i++) { + if (metaTags[i].getAttribute('name') === 'viewport') { + viewportTag = metaTags[i]; + break; + } + } + if (!viewportTag) { + viewportTag = document.createElement('meta'); + viewportTag.setAttribute('name', 'viewport'); + } + + var viewportTagContent = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no'; + + // + // Detect support for CSS env() variable + // + var envTestDiv = '
'; + + document.body.insertAdjacentHTML('afterbegin', envTestDiv); + + var testElement = document.getElementById('envTest'); + var testResult = common.getStyle(testElement, 'margin-top'); + document.body.removeChild(testElement); + + // if browser supports env(), returns a pixel value as string, even if 0px + if (testResult != '-99px') { + viewportTagContent += ', viewport-fit=cover'; + } + + // Update viewport tag attribute + viewportTag.setAttribute('content', viewportTagContent); + + /***************************************************************************** + * Prevent background, background-color, background-image properties + *****************************************************************************/ + var cssAdjuster = document.createElement('style'); + cssAdjuster.setAttribute('type', 'text/css'); + cssAdjuster.innerText = [ + 'html, body, ._gmaps_cdv_ {', + ' background-image: none !important;', + ' background: rgba(0,0,0,0) none !important;', + ' background-color: rgba(0,0,0,0) !important;', + '}', + '._gmaps_cdv_ .nav-decor {', + ' background-color: rgba(0,0,0,0) !important;', + ' background: rgba(0,0,0,0) none !important;', + ' display:none !important;', + '}', + '.framework7-root .page-previous {', + ' display:none !important;', + '}' + ].join(''); + document.head.appendChild(cssAdjuster); + + // I guess no longer necessary this code at 2018/March + // //---------------------------------------------- + // // Set transparent mandatory for older browser + // // http://stackoverflow.com/a/3485654/697856 + // //---------------------------------------------- + // if(document.body){ + // document.body.style.backgroundColor = 'rgba(0,0,0,0)'; + // //document.body.style.display='none'; + // document.body.offsetHeight; + // //document.body.style.display=''; + // } + + //-------------------------------------------- + // Hook the backbutton of Android action + //-------------------------------------------- + var anotherBackbuttonHandler = null; + + function onBackButton(e) { + + // Check DOM tree for new page + cordova.fireDocumentEvent('plugin_touch', { + force: true + }); + + if (anotherBackbuttonHandler) { + // anotherBackbuttonHandler must handle the page moving transaction. + // The plugin does not take care anymore if another callback is registered. + anotherBackbuttonHandler(e); + } else { + cordova_exec(null, null, 'CordovaGoogleMaps', 'backHistory', []); + } + } + + document.addEventListener('backbutton', onBackButton); + + var _org_addEventListener = document.addEventListener; + var _org_removeEventListener = document.removeEventListener; + document.addEventListener = function (eventName, callback) { + var args = Array.prototype.slice.call(arguments, 0); + if (eventName.toLowerCase() !== 'backbutton') { + _org_addEventListener.apply(this, args); + return; + } + if (!anotherBackbuttonHandler) { + anotherBackbuttonHandler = callback; + } + }; + document.removeEventListener = function (eventName, callback) { + var args = Array.prototype.slice.call(arguments, 0); + if (eventName.toLowerCase() !== 'backbutton') { + _org_removeEventListener.apply(this, args); + return; + } + if (anotherBackbuttonHandler === callback) { + anotherBackbuttonHandler = null; + } + }; + +} + +module.exports = pluginInit; diff --git a/node_modules/cordova-plugin-googlemaps/www/poly.js b/node_modules/cordova-plugin-googlemaps/www/poly.js new file mode 100644 index 0000000..26d882e --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/poly.js @@ -0,0 +1,106 @@ +var LatLngBounds = require('./LatLngBounds'), + BaseArrayClass = require('./BaseArrayClass'); + +function containsLocation(latLng, path) { + if ('lat' in latLng === false || + 'lng' in latLng === false) { + return false; + } + if (path instanceof BaseArrayClass) { + path = path.getArray(); + } + var points = JSON.parse(JSON.stringify(path)); + + var first = points[0], + last = points[points.length - 1]; + if (first.lat !== last.lat || first.lng !== last.lng) { + points.push({ + lat: first.lat, + lng: first.lng + }); + } + var point = { + lat: latLng.lat, + lng: latLng.lng + }; + + var wn = 0, + bounds = new LatLngBounds(points), + sw = bounds.southwest, + ne = bounds.northeast, + offsetLng360 = sw.lng <= 0 && ne.lng >= 0 && sw.lng < ne.lng ? 360 : 0; + + sw.lng += offsetLng360; + point.lng += offsetLng360; + + points = points.map(function(vertex) { + vertex.lng += +offsetLng360; + return vertex; + }); + + var vt, a, b; + + for (var i = 0; i < points.length - 1; i++) { + a = points[i]; + b = points[i + 1]; + + if ((a.lat <= point.lat) && (b.lat > point.lat)) { + vt = (point.lat - a.lat) / (b.lat - a.lat); + if (point.lng < (a.lng + (vt * (b.lng - a.lng)))) { + wn++; + } + } else if ((a.lat > point.lat) && (b.lat <= point.lat)) { + vt = (point.lat - a.lat) / (b.lat - a.lat); + if (point.lng < (a.lng + (vt * (b.lng - a.lng)))) { + wn--; + } + } + } + + return (wn !== 0); +} + +function isLocationOnEdge(latLng, path) { + if ('lat' in latLng === false || + 'lng' in latLng === false) { + return false; + } + + var Sx, Sy; + var p0, p1; + var point = {'lat': latLng.lat, 'lng': latLng.lng}; + + if (path instanceof BaseArrayClass) { + path = path.getArray(); + } + var points = JSON.parse(JSON.stringify(path)); + + var bounds = new LatLngBounds(points), + sw = bounds.southwest, + ne = bounds.northeast, + offsetLng360 = sw.lng <= 0 && ne.lng >= 0 && sw.lng < ne.lng ? 360 : 0; + + point.lng += offsetLng360; + + points = points.map(function(vertex) { + vertex.lng += offsetLng360; + return vertex; + }); + + p0 = points[0]; + for (var i = 1; i < points.length; i++) { + p1 = points[i]; + Sx = (point.lng - p0.lng) / (p1.lng - p0.lng); + Sy = (point.lat - p0.lat) / (p1.lat - p0.lat); + if (Math.abs(Sx - Sy) < 0.05 && Sx < 1 && Sx > 0) { + return true; + } + p0 = p1; + } + return false; +} + +module.exports = { + containsLocation: containsLocation, + isLocationOnEdge: isLocationOnEdge +}; diff --git a/node_modules/cordova-plugin-googlemaps/www/spherical.js b/node_modules/cordova-plugin-googlemaps/www/spherical.js new file mode 100644 index 0000000..04a08c9 --- /dev/null +++ b/node_modules/cordova-plugin-googlemaps/www/spherical.js @@ -0,0 +1,281 @@ +var LatLng = require('./LatLng'); +var common = require('./Common'); +var EARTH_RADIUS = 6371009; + +/** + * Port from android-maps-utils + * https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/SphericalUtil.java + */ + +/** + * Returns the non-negative remainder of x / m. + * @param x The operand. + * @param m The modulus. + */ +function mod(x, m) { + return ((x % m) + m) % m; +} +/** + * Wraps the given value into the inclusive-exclusive interval between min and max. + * @param n The value to wrap. + * @param min The minimum. + * @param max The maximum. + */ +function wrap(n, min, max) { + return (n >= min && n < max) ? n : (mod(n - min, max - min) + min); +} + +/** + * Returns haversine(angle-in-radians). + * hav(x) == (1 - cos(x)) / 2 == sin(x / 2)^2. + */ +function hav(x) { + var sinHalf = Math.sin(x * 0.5); + return sinHalf * sinHalf; +} + +/** + * Computes inverse haversine. Has good numerical stability around 0. + * arcHav(x) == acos(1 - 2 * x) == 2 * asin(sqrt(x)). + * The argument must be in [0, 1], and the result is positive. + */ +function arcHav(x) { + return 2 * Math.asin(Math.sqrt(x)); +} + +/** + * Returns hav() of distance from (lat1, lng1) to (lat2, lng2) on the unit sphere. + */ +function havDistance(lat1, lat2, dLng) { + return hav(lat1 - lat2) + hav(dLng) * Math.cos(lat1) * Math.cos(lat2); +} + +/** + * Returns distance on the unit sphere; the arguments are in radians. + */ +function distanceRadians(lat1, lng1, lat2, lng2) { + return arcHav(havDistance(lat1, lat2, lng1 - lng2)); +} + +/** + * Returns the angle between two LatLngs, in radians. This is the same as the distance + * on the unit sphere. + */ +function computeAngleBetween(from, to) { + return distanceRadians(toRadians(from.lat), toRadians(from.lng), + toRadians(to.lat), toRadians(to.lng)); +} + +/** + * Returns the distance between two LatLngs, in meters. + */ +function computeDistanceBetween(from, to) { + return computeAngleBetween(from, to) * EARTH_RADIUS; +} + +/** + * Returns the distance between two LatLngs, in meters. + */ +function computeOffset(from, distance, heading) { + distance /= EARTH_RADIUS; + heading = toRadians(heading); + + // http://williams.best.vwh.net/avform.htm#LL + var fromLat = toRadians(from.lat); + var fromLng = toRadians(from.lng); + var cosDistance = Math.cos(distance); + var sinDistance = Math.sin(distance); + var sinFromLat = Math.sin(fromLat); + var cosFromLat = Math.cos(fromLat); + var sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * Math.cos(heading); + var dLng = Math.atan2( + sinDistance * cosFromLat * Math.sin(heading), + cosDistance - sinFromLat * sinLat); + return new LatLng(toDegrees(Math.asin(sinLat)), toDegrees(fromLng + dLng)); +} + +function toRadians(d) { + return d * Math.PI / 180; +} + +function toDegrees(r) { + return r * 180 / Math.PI; +} + +/* + * Returns the signed area of a closed path on a sphere of given radius. + */ +function computeSignedArea(path) { + var radius = EARTH_RADIUS; + path = common.convertToPositionArray(path); + + var size = path.length; + if (size < 3) { + return 0; + } + var total = 0; + + var prev = path[size - 1]; + var prevTanLat = Math.tan((Math.PI / 2 - toRadians(prev.lat)) / 2); + var prevLng = toRadians(prev.lng); + + // For each edge, accumulate the signed area of the triangle formed by the North Pole + // and that edge ("polar triangle"). + path.forEach(function (position) { + var tanLat = Math.tan((Math.PI / 2 - toRadians(position.lat)) / 2); + var lng = toRadians(position.lng); + total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng); + prevTanLat = tanLat; + prevLng = lng; + }); + return total * (radius * radius); +} + +/* + * Returns the signed area of a triangle which has North Pole as a vertex. + * Formula derived from "Area of a spherical triangle given two edges and the included angle" + * as per "Spherical Trigonometry" by Todhunter, page 71, section 103, point 2. + * See http://books.google.com/books?id=3uBHAAAAIAAJ&pg=PA71 + * The arguments named "tan" are tan((pi/2 - latitude)/2). + */ +function polarTriangleArea(tan1, lng1, tan2, lng2) { + + var deltaLng = lng1 - lng2; + var t = tan1 * tan2; + return 2 * Math.atan2(t * Math.sin(deltaLng), 1 + t * Math.cos(deltaLng)); +} + +function computeArea(path) { + return Math.abs(computeSignedArea(path)); +} + +/** + * Returns the heading from one LatLng to another LatLng. Headings are + * expressed in degrees clockwise from North within the range [-180,180). + * @return The heading in degrees clockwise from north. + */ +function computeHeading(from, to) { + // http://williams.best.vwh.net/avform.htm#Crs + var fromLat = toRadians(from.lat); + var fromLng = toRadians(from.lng); + var toLat = toRadians(to.lat); + var toLng = toRadians(to.lng); + var dLng = toLng - fromLng; + var heading = Math.atan2( + Math.sin(dLng) * Math.cos(toLat), + Math.cos(fromLat) * Math.sin(toLat) - Math.sin(fromLat) * Math.cos(toLat) * Math.cos(dLng)); + return wrap(toDegrees(heading), -180, 180); +} + +/** + * Returns the location of origin when provided with a LatLng destination, + * meters travelled and original heading. Headings are expressed in degrees + * clockwise from North. This function returns null when no solution is + * available. + * @param to The destination LatLng. + * @param distance The distance travelled, in meters. + * @param heading The heading in degrees clockwise from north. + */ +function computeOffsetOrigin(to, distance, heading) { + heading = toRadians(heading); + distance /= EARTH_RADIUS; + // http://lists.maptools.org/pipermail/proj/2008-October/003939.html + var n1 = Math.cos(distance); + var n2 = Math.sin(distance) * Math.cos(heading); + var n3 = Math.sin(distance) * Math.sin(heading); + var n4 = Math.sin(toRadians(to.lat)); + // There are two solutions for b. b = n2 * n4 +/- sqrt(), one solution results + // in the latitude outside the [-90, 90] range. We first try one solution and + // back off to the other if we are outside that range. + var n12 = n1 * n1; + var discriminant = n2 * n2 * n12 + n12 * n12 - n12 * n4 * n4; + if (discriminant < 0) { + // No real solution which would make sense in LatLng-space. + return null; + } + var b = n2 * n4 + Math.sqrt(discriminant); + b /= n1 * n1 + n2 * n2; + var a = (n4 - n2 * b) / n1; + var fromLatRadians = Math.atan2(a, b); + if (fromLatRadians < -Math.PI / 2 || fromLatRadians > Math.PI / 2) { + b = n2 * n4 - Math.sqrt(discriminant); + b /= n1 * n1 + n2 * n2; + fromLatRadians = Math.atan2(a, b); + } + if (fromLatRadians < -Math.PI / 2 || fromLatRadians > Math.PI / 2) { + // No solution which would make sense in LatLng-space. + return null; + } + var fromLngRadians = toRadians(to.lng) - + Math.atan2(n3, n1 * Math.cos(fromLatRadians) - n2 * Math.sin(fromLatRadians)); + return new LatLng(toDegrees(fromLatRadians), toDegrees(fromLngRadians)); +} + +/** + * Returns the LatLng which lies the given fraction of the way between the + * origin LatLng and the destination LatLng. + * @param from The LatLng from which to start. + * @param to The LatLng toward which to travel. + * @param fraction A fraction of the distance to travel. + * @return The interpolated LatLng. + */ +function interpolate(from, to, fraction) { + // http://en.wikipedia.org/wiki/Slerp + var fromLat = toRadians(from.lat); + var fromLng = toRadians(from.lng); + var toLat = toRadians(to.lat); + var toLng = toRadians(to.lng); + var cosFromLat = Math.cos(fromLat); + var cosToLat = Math.cos(toLat); + + // Computes Spherical interpolation coefficients. + var angle = computeAngleBetween(from, to); + var sinAngle = Math.sin(angle); + if (sinAngle < 1E-6) { + return from; + } + var a = Math.sin((1 - fraction) * angle) / sinAngle; + var b = Math.sin(fraction * angle) / sinAngle; + + // Converts from polar to vector and interpolate. + var x = a * cosFromLat * Math.cos(fromLng) + b * cosToLat * Math.cos(toLng); + var y = a * cosFromLat * Math.sin(fromLng) + b * cosToLat * Math.sin(toLng); + var z = a * Math.sin(fromLat) + b * Math.sin(toLat); + + // Converts interpolated vector back to polar. + var lat = Math.atan2(z, Math.sqrt(x * x + y * y)); + var lng = Math.atan2(y, x); + return new LatLng(toDegrees(lat), toDegrees(lng)); +} + +/** + * Returns the length of the given path, in meters, on Earth. + */ +function computeLength(path) { + path = common.convertToPositionArray(path); + if (path.length < 2) { + return 0; + } + var length = 0; + var prev = path[0]; + var prevLat = toRadians(prev.lat); + var prevLng = toRadians(prev.lng); + path.forEach(function (point) { + var lat = toRadians(point.lat); + var lng = toRadians(point.lng); + length += distanceRadians(prevLat, prevLng, lat, lng); + prevLat = lat; + prevLng = lng; + }); + return length * EARTH_RADIUS; +} +module.exports = { + computeDistanceBetween: computeDistanceBetween, + computeOffset: computeOffset, + computeOffsetOrigin: computeOffsetOrigin, + computeArea: computeArea, + computeSignedArea: computeSignedArea, + computeHeading: computeHeading, + interpolate: interpolate, + computeLength: computeLength +}; diff --git a/node_modules/cross-spawn/LICENSE b/node_modules/cross-spawn/LICENSE new file mode 100644 index 0000000..8407b9a --- /dev/null +++ b/node_modules/cross-spawn/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Made With MOXY Lda + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/cross-spawn/README.md b/node_modules/cross-spawn/README.md new file mode 100644 index 0000000..e895cd7 --- /dev/null +++ b/node_modules/cross-spawn/README.md @@ -0,0 +1,94 @@ +# cross-spawn + +[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Build status][appveyor-image]][appveyor-url] [![Coverage Status][codecov-image]][codecov-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url] [![Greenkeeper badge][greenkeeper-image]][greenkeeper-url] + +[npm-url]:https://npmjs.org/package/cross-spawn +[downloads-image]:http://img.shields.io/npm/dm/cross-spawn.svg +[npm-image]:http://img.shields.io/npm/v/cross-spawn.svg +[travis-url]:https://travis-ci.org/moxystudio/node-cross-spawn +[travis-image]:http://img.shields.io/travis/moxystudio/node-cross-spawn/master.svg +[appveyor-url]:https://ci.appveyor.com/project/satazor/node-cross-spawn +[appveyor-image]:https://img.shields.io/appveyor/ci/satazor/node-cross-spawn/master.svg +[codecov-url]:https://codecov.io/gh/moxystudio/node-cross-spawn +[codecov-image]:https://img.shields.io/codecov/c/github/moxystudio/node-cross-spawn/master.svg +[david-dm-url]:https://david-dm.org/moxystudio/node-cross-spawn +[david-dm-image]:https://img.shields.io/david/moxystudio/node-cross-spawn.svg +[david-dm-dev-url]:https://david-dm.org/moxystudio/node-cross-spawn?type=dev +[david-dm-dev-image]:https://img.shields.io/david/dev/moxystudio/node-cross-spawn.svg +[greenkeeper-image]:https://badges.greenkeeper.io/moxystudio/node-cross-spawn.svg +[greenkeeper-url]:https://greenkeeper.io/ + +A cross platform solution to node's spawn and spawnSync. + + +## Installation + +`$ npm install cross-spawn` + + +## Why + +Node has issues when using spawn on Windows: + +- It ignores [PATHEXT](https://github.com/joyent/node/issues/2318) +- It does not support [shebangs](https://en.wikipedia.org/wiki/Shebang_(Unix)) +- Has problems running commands with [spaces](https://github.com/nodejs/node/issues/7367) +- Has problems running commands with posix relative paths (e.g.: `./my-folder/my-executable`) +- Has an [issue](https://github.com/moxystudio/node-cross-spawn/issues/82) with command shims (files in `node_modules/.bin/`), where arguments with quotes and parenthesis would result in [invalid syntax error](https://github.com/moxystudio/node-cross-spawn/blob/e77b8f22a416db46b6196767bcd35601d7e11d54/test/index.test.js#L149) +- No `options.shell` support on node `` where `` must not contain any arguments. +If you would like to have the shebang support improved, feel free to contribute via a pull-request. + +Remember to always test your code on Windows! + + +## Tests + +`$ npm test` +`$ npm test -- --watch` during development + +## License + +Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php). diff --git a/node_modules/cross-spawn/index.js b/node_modules/cross-spawn/index.js new file mode 100644 index 0000000..5509742 --- /dev/null +++ b/node_modules/cross-spawn/index.js @@ -0,0 +1,39 @@ +'use strict'; + +const cp = require('child_process'); +const parse = require('./lib/parse'); +const enoent = require('./lib/enoent'); + +function spawn(command, args, options) { + // Parse the arguments + const parsed = parse(command, args, options); + + // Spawn the child process + const spawned = cp.spawn(parsed.command, parsed.args, parsed.options); + + // Hook into child process "exit" event to emit an error if the command + // does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 + enoent.hookChildProcess(spawned, parsed); + + return spawned; +} + +function spawnSync(command, args, options) { + // Parse the arguments + const parsed = parse(command, args, options); + + // Spawn the child process + const result = cp.spawnSync(parsed.command, parsed.args, parsed.options); + + // Analyze if the command does not exist, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 + result.error = result.error || enoent.verifyENOENTSync(result.status, parsed); + + return result; +} + +module.exports = spawn; +module.exports.spawn = spawn; +module.exports.sync = spawnSync; + +module.exports._parse = parse; +module.exports._enoent = enoent; diff --git a/node_modules/cross-spawn/lib/enoent.js b/node_modules/cross-spawn/lib/enoent.js new file mode 100644 index 0000000..14df9b6 --- /dev/null +++ b/node_modules/cross-spawn/lib/enoent.js @@ -0,0 +1,59 @@ +'use strict'; + +const isWin = process.platform === 'win32'; + +function notFoundError(original, syscall) { + return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), { + code: 'ENOENT', + errno: 'ENOENT', + syscall: `${syscall} ${original.command}`, + path: original.command, + spawnargs: original.args, + }); +} + +function hookChildProcess(cp, parsed) { + if (!isWin) { + return; + } + + const originalEmit = cp.emit; + + cp.emit = function (name, arg1) { + // If emitting "exit" event and exit code is 1, we need to check if + // the command exists and emit an "error" instead + // See https://github.com/IndigoUnited/node-cross-spawn/issues/16 + if (name === 'exit') { + const err = verifyENOENT(arg1, parsed, 'spawn'); + + if (err) { + return originalEmit.call(cp, 'error', err); + } + } + + return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params + }; +} + +function verifyENOENT(status, parsed) { + if (isWin && status === 1 && !parsed.file) { + return notFoundError(parsed.original, 'spawn'); + } + + return null; +} + +function verifyENOENTSync(status, parsed) { + if (isWin && status === 1 && !parsed.file) { + return notFoundError(parsed.original, 'spawnSync'); + } + + return null; +} + +module.exports = { + hookChildProcess, + verifyENOENT, + verifyENOENTSync, + notFoundError, +}; diff --git a/node_modules/cross-spawn/lib/parse.js b/node_modules/cross-spawn/lib/parse.js new file mode 100644 index 0000000..962827a --- /dev/null +++ b/node_modules/cross-spawn/lib/parse.js @@ -0,0 +1,125 @@ +'use strict'; + +const path = require('path'); +const niceTry = require('nice-try'); +const resolveCommand = require('./util/resolveCommand'); +const escape = require('./util/escape'); +const readShebang = require('./util/readShebang'); +const semver = require('semver'); + +const isWin = process.platform === 'win32'; +const isExecutableRegExp = /\.(?:com|exe)$/i; +const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i; + +// `options.shell` is supported in Node ^4.8.0, ^5.7.0 and >= 6.0.0 +const supportsShellOption = niceTry(() => semver.satisfies(process.version, '^4.8.0 || ^5.7.0 || >= 6.0.0', true)) || false; + +function detectShebang(parsed) { + parsed.file = resolveCommand(parsed); + + const shebang = parsed.file && readShebang(parsed.file); + + if (shebang) { + parsed.args.unshift(parsed.file); + parsed.command = shebang; + + return resolveCommand(parsed); + } + + return parsed.file; +} + +function parseNonShell(parsed) { + if (!isWin) { + return parsed; + } + + // Detect & add support for shebangs + const commandFile = detectShebang(parsed); + + // We don't need a shell if the command filename is an executable + const needsShell = !isExecutableRegExp.test(commandFile); + + // If a shell is required, use cmd.exe and take care of escaping everything correctly + // Note that `forceShell` is an hidden option used only in tests + if (parsed.options.forceShell || needsShell) { + // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/` + // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument + // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called, + // we need to double escape them + const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile); + + // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar) + // This is necessary otherwise it will always fail with ENOENT in those cases + parsed.command = path.normalize(parsed.command); + + // Escape command & arguments + parsed.command = escape.command(parsed.command); + parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars)); + + const shellCommand = [parsed.command].concat(parsed.args).join(' '); + + parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`]; + parsed.command = process.env.comspec || 'cmd.exe'; + parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped + } + + return parsed; +} + +function parseShell(parsed) { + // If node supports the shell option, there's no need to mimic its behavior + if (supportsShellOption) { + return parsed; + } + + // Mimic node shell option + // See https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335 + const shellCommand = [parsed.command].concat(parsed.args).join(' '); + + if (isWin) { + parsed.command = typeof parsed.options.shell === 'string' ? parsed.options.shell : process.env.comspec || 'cmd.exe'; + parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`]; + parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped + } else { + if (typeof parsed.options.shell === 'string') { + parsed.command = parsed.options.shell; + } else if (process.platform === 'android') { + parsed.command = '/system/bin/sh'; + } else { + parsed.command = '/bin/sh'; + } + + parsed.args = ['-c', shellCommand]; + } + + return parsed; +} + +function parse(command, args, options) { + // Normalize arguments, similar to nodejs + if (args && !Array.isArray(args)) { + options = args; + args = null; + } + + args = args ? args.slice(0) : []; // Clone array to avoid changing the original + options = Object.assign({}, options); // Clone object to avoid changing the original + + // Build our parsed object + const parsed = { + command, + args, + options, + file: undefined, + original: { + command, + args, + }, + }; + + // Delegate further parsing to shell or non-shell + return options.shell ? parseShell(parsed) : parseNonShell(parsed); +} + +module.exports = parse; diff --git a/node_modules/cross-spawn/lib/util/escape.js b/node_modules/cross-spawn/lib/util/escape.js new file mode 100644 index 0000000..7bf2905 --- /dev/null +++ b/node_modules/cross-spawn/lib/util/escape.js @@ -0,0 +1,47 @@ +'use strict'; + +// See http://www.robvanderwoude.com/escapechars.php +const metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g; + +function escapeCommand(arg) { + // Escape meta chars + arg = arg.replace(metaCharsRegExp, '^$1'); + + return arg; +} + +function escapeArgument(arg, doubleEscapeMetaChars) { + // Convert to string + arg = `${arg}`; + + // Algorithm below is based on https://qntm.org/cmd + // It's slightly altered to disable JS backtracking to avoid hanging on specially crafted input + // Please see https://github.com/moxystudio/node-cross-spawn/pull/160 for more information + + // Sequence of backslashes followed by a double quote: + // double up all the backslashes and escape the double quote + arg = arg.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"'); + + // Sequence of backslashes followed by the end of the string + // (which will become a double quote later): + // double up all the backslashes + arg = arg.replace(/(?=(\\+?)?)\1$/, '$1$1'); + + // All other backslashes occur literally + + // Quote the whole thing: + arg = `"${arg}"`; + + // Escape meta chars + arg = arg.replace(metaCharsRegExp, '^$1'); + + // Double escape meta chars if necessary + if (doubleEscapeMetaChars) { + arg = arg.replace(metaCharsRegExp, '^$1'); + } + + return arg; +} + +module.exports.command = escapeCommand; +module.exports.argument = escapeArgument; diff --git a/node_modules/cross-spawn/lib/util/readShebang.js b/node_modules/cross-spawn/lib/util/readShebang.js new file mode 100644 index 0000000..bd4f128 --- /dev/null +++ b/node_modules/cross-spawn/lib/util/readShebang.js @@ -0,0 +1,32 @@ +'use strict'; + +const fs = require('fs'); +const shebangCommand = require('shebang-command'); + +function readShebang(command) { + // Read the first 150 bytes from the file + const size = 150; + let buffer; + + if (Buffer.alloc) { + // Node.js v4.5+ / v5.10+ + buffer = Buffer.alloc(size); + } else { + // Old Node.js API + buffer = new Buffer(size); + buffer.fill(0); // zero-fill + } + + let fd; + + try { + fd = fs.openSync(command, 'r'); + fs.readSync(fd, buffer, 0, size, 0); + fs.closeSync(fd); + } catch (e) { /* Empty */ } + + // Attempt to extract shebang (null is returned if not a shebang) + return shebangCommand(buffer.toString()); +} + +module.exports = readShebang; diff --git a/node_modules/cross-spawn/lib/util/resolveCommand.js b/node_modules/cross-spawn/lib/util/resolveCommand.js new file mode 100644 index 0000000..c46d8d7 --- /dev/null +++ b/node_modules/cross-spawn/lib/util/resolveCommand.js @@ -0,0 +1,51 @@ +'use strict'; + +const path = require('path'); +const which = require('which'); +const pathKey = require('path-key')(); + +function resolveCommandAttempt(parsed, withoutPathExt) { + const cwd = process.cwd(); + const hasCustomCwd = parsed.options.cwd != null; + // Worker threads do not have process.chdir() + const shouldSwitchCwd = hasCustomCwd && process.chdir !== undefined; + + // If a custom `cwd` was specified, we need to change the process cwd + // because `which` will do stat calls but does not support a custom cwd + if (shouldSwitchCwd) { + try { + process.chdir(parsed.options.cwd); + } catch (err) { + /* Empty */ + } + } + + let resolved; + + try { + resolved = which.sync(parsed.command, { + path: (parsed.options.env || process.env)[pathKey], + pathExt: withoutPathExt ? path.delimiter : undefined, + }); + } catch (e) { + /* Empty */ + } finally { + if (shouldSwitchCwd) { + process.chdir(cwd); + } + } + + // If we successfully resolved, ensure that an absolute path is returned + // Note that when a custom `cwd` was used, we need to resolve to an absolute path based on it + if (resolved) { + resolved = path.resolve(hasCustomCwd ? parsed.options.cwd : '', resolved); + } + + return resolved; +} + +function resolveCommand(parsed) { + return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true); +} + +module.exports = resolveCommand; diff --git a/node_modules/cross-spawn/package.json b/node_modules/cross-spawn/package.json new file mode 100644 index 0000000..e5d8000 --- /dev/null +++ b/node_modules/cross-spawn/package.json @@ -0,0 +1,75 @@ +{ + "name": "cross-spawn", + "version": "6.0.6", + "description": "Cross platform child_process#spawn and child_process#spawnSync", + "keywords": [ + "spawn", + "spawnSync", + "windows", + "cross-platform", + "path-ext", + "shebang", + "cmd", + "execute" + ], + "author": "André Cruz ", + "homepage": "https://github.com/moxystudio/node-cross-spawn", + "repository": { + "type": "git", + "url": "git@github.com:moxystudio/node-cross-spawn.git" + }, + "license": "MIT", + "main": "index.js", + "files": [ + "lib" + ], + "scripts": { + "lint": "eslint .", + "test": "jest --env node --coverage", + "release": "standard-version", + "precommit": "lint-staged", + "commitmsg": "commitlint -e $GIT_PARAMS" + }, + "standard-version": { + "scripts": { + "posttag": "git push --follow-tags origin v6" + } + }, + "lint-staged": { + "*.js": [ + "eslint --fix", + "git add" + ] + }, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "devDependencies": { + "@commitlint/cli": "^6.0.0", + "@commitlint/config-conventional": "^6.0.2", + "babel-core": "^6.26.0", + "babel-jest": "^22.1.0", + "babel-preset-moxy": "^2.2.1", + "eslint": "^4.3.0", + "eslint-config-moxy": "^5.0.0", + "husky": "^0.14.3", + "jest": "^22.0.0", + "lint-staged": "^7.0.0", + "mkdirp": "^0.5.1", + "regenerator-runtime": "^0.11.1", + "rimraf": "^2.6.2", + "standard-version": "^4.2.0" + }, + "engines": { + "node": ">=4.8" + } +} diff --git a/node_modules/dedent/LICENSE b/node_modules/dedent/LICENSE new file mode 100644 index 0000000..603995b --- /dev/null +++ b/node_modules/dedent/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Desmond Brand (dmnd@desmondbrand.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/dedent/README.md b/node_modules/dedent/README.md new file mode 100644 index 0000000..af51323 --- /dev/null +++ b/node_modules/dedent/README.md @@ -0,0 +1,59 @@ +# Dedent + +An ES6 string tag that strips indentation from multi-line strings. + +## Usage + +```js +import dedent from "dedent"; + +function usageExample() { + const first = dedent`A string that gets so long you need to break it over + multiple lines. Luckily dedent is here to keep it + readable without lots of spaces ending up in the string + itself.`; + + const second = dedent` + Leading and trailing lines will be trimmed, so you can write something like + this and have it work as you expect: + + * how convenient it is + * that I can use an indented list + - and still have it do the right thing + + That's all. + `; + + const third = dedent(` + Wait! I lied. Dedent can also be used as a function. + `); + + return first + "\n\n" + second + "\n\n" + third; +} +``` + +```js +> console.log(usageExample()); +``` + +``` +A string that gets so long you need to break it over +multiple lines. Luckily dedent is here to keep it +readable without lots of spaces ending up in the string +itself. + +Leading and trailing lines will be trimmed, so you can write something like +this and have it work as you expect: + + * how convenient it is + * that I can use an indented list + - and still have it do the right thing + +That's all. + +Wait! I lied. Dedent can also be used as a function. +``` + +## License + +MIT diff --git a/node_modules/dedent/dist/dedent.js b/node_modules/dedent/dist/dedent.js new file mode 100644 index 0000000..8979b57 --- /dev/null +++ b/node_modules/dedent/dist/dedent.js @@ -0,0 +1,59 @@ +"use strict"; + +function dedent(strings) { + + var raw = void 0; + if (typeof strings === "string") { + // dedent can be used as a plain function + raw = [strings]; + } else { + raw = strings.raw; + } + + // first, perform interpolation + var result = ""; + for (var i = 0; i < raw.length; i++) { + result += raw[i]. + // join lines when there is a suppressed newline + replace(/\\\n[ \t]*/g, ""). + + // handle escaped backticks + replace(/\\`/g, "`"); + + if (i < (arguments.length <= 1 ? 0 : arguments.length - 1)) { + result += arguments.length <= i + 1 ? undefined : arguments[i + 1]; + } + } + + // now strip indentation + var lines = result.split("\n"); + var mindent = null; + lines.forEach(function (l) { + var m = l.match(/^(\s+)\S+/); + if (m) { + var indent = m[1].length; + if (!mindent) { + // this is the first indented line + mindent = indent; + } else { + mindent = Math.min(mindent, indent); + } + } + }); + + if (mindent !== null) { + result = lines.map(function (l) { + return l[0] === " " ? l.slice(mindent) : l; + }).join("\n"); + } + + // dedent eats leading and trailing whitespace too + result = result.trim(); + + // handle escaped newlines at the end to ensure they don't get stripped too + return result.replace(/\\n/g, "\n"); +} + +if (typeof module !== "undefined") { + module.exports = dedent; +} diff --git a/node_modules/dedent/package.json b/node_modules/dedent/package.json new file mode 100644 index 0000000..330c43a --- /dev/null +++ b/node_modules/dedent/package.json @@ -0,0 +1,43 @@ +{ + "name": "dedent", + "version": "0.7.0", + "description": "An ES6 string tag that strips indentation from multi-line strings", + "main": "dist/dedent.js", + "files": [ + "dist/dedent.js", + "LICENSE" + ], + "repository": { + "type": "git", + "url": "git://github.com/dmnd/dedent.git" + }, + "keywords": [ + "dedent", + "tag", + "multi-line string", + "es6" + ], + "author": { + "name": "Desmond Brand", + "email": "dmnd@desmondbrand.com", + "url": "http://desmondbrand.com" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/dmnd/dedent/issues" + }, + "homepage": "https://github.com/dmnd/dedent", + "devDependencies": { + "babel-cli": "^6.22.2", + "babel-preset-es2015": "^6.22.0", + "babel-preset-es2016": "^6.22.0", + "babel-preset-es2017": "^6.22.0", + "eslint": "^3.14.1", + "jest": "^18.1.0" + }, + "scripts": { + "build": "babel dedent.js --out-file dist/dedent.js", + "lint": "eslint dedent.js __tests__", + "test": "jest" + } +} diff --git a/node_modules/elementtree/.npmignore b/node_modules/elementtree/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/node_modules/elementtree/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/node_modules/elementtree/.travis.yml b/node_modules/elementtree/.travis.yml new file mode 100644 index 0000000..1ab2652 --- /dev/null +++ b/node_modules/elementtree/.travis.yml @@ -0,0 +1,14 @@ +language: node_js +sudo: false + +node_js: + - "0.10" + - "0.11" + - "0.12" + - "iojs" + +script: make test + +notifications: + email: + - tomaz+travisci@tomaz.me diff --git a/node_modules/elementtree/CHANGES.md b/node_modules/elementtree/CHANGES.md new file mode 100644 index 0000000..08fdb1f --- /dev/null +++ b/node_modules/elementtree/CHANGES.md @@ -0,0 +1,39 @@ +elementtree v0.1.6 - 2014-02-06 + +* Add support for CData elements. (#14) + [hermannpencole] + +elementtree v0.1.5 - 2012-11-14 + +* Fix a bug in the find() and findtext() method which could manifest itself + under some conditions. + [metagriffin] + +elementtree v0.1.4 - 2012-10-15 + +* Allow user to use namespaced attributes when using find* functions. + [Andrew Lunny] + +elementtree v0.1.3 - 2012-09-21 + +* Improve the output of text content in the tags (strip unnecessary line break + characters). + +[Darryl Pogue] + +elementtree v0.1.2 - 2012-09-04 + + * Allow user to pass 'indent' option to ElementTree.write method. If this + option is specified (e.g. {'indent': 4}). XML will be pretty printed. + [Darryl Pogue, Tomaz Muraus] + + * Bump sax dependency version. + +elementtree v0.1.1 - 2011-09-23 + + * Improve special character escaping. + [Ryan Phillips] + +elementtree v0.1.0 - 2011-09-05 + + * Initial release. diff --git a/node_modules/elementtree/LICENSE.txt b/node_modules/elementtree/LICENSE.txt new file mode 100644 index 0000000..6b0b127 --- /dev/null +++ b/node_modules/elementtree/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/node_modules/elementtree/Makefile b/node_modules/elementtree/Makefile new file mode 100644 index 0000000..ab7c4e0 --- /dev/null +++ b/node_modules/elementtree/Makefile @@ -0,0 +1,21 @@ +TESTS := \ + tests/test-simple.js + + + +PATH := ./node_modules/.bin:$(PATH) + +WHISKEY := $(shell bash -c 'PATH=$(PATH) type -p whiskey') + +default: test + +test: + NODE_PATH=`pwd`/lib/ ${WHISKEY} --scope-leaks --sequential --real-time --tests "${TESTS}" + +tap: + NODE_PATH=`pwd`/lib/ ${WHISKEY} --test-reporter tap --sequential --real-time --tests "${TESTS}" + +coverage: + NODE_PATH=`pwd`/lib/ ${WHISKEY} --sequential --coverage --coverage-reporter html --coverage-dir coverage_html --tests "${TESTS}" + +.PHONY: default test coverage tap scope diff --git a/node_modules/elementtree/NOTICE b/node_modules/elementtree/NOTICE new file mode 100644 index 0000000..28ad70a --- /dev/null +++ b/node_modules/elementtree/NOTICE @@ -0,0 +1,5 @@ +node-elementtree +Copyright (c) 2011, Rackspace, Inc. + +The ElementTree toolkit is Copyright (c) 1999-2007 by Fredrik Lundh + diff --git a/node_modules/elementtree/README.md b/node_modules/elementtree/README.md new file mode 100644 index 0000000..738420c --- /dev/null +++ b/node_modules/elementtree/README.md @@ -0,0 +1,141 @@ +node-elementtree +==================== + +node-elementtree is a [Node.js](http://nodejs.org) XML parser and serializer based upon the [Python ElementTree v1.3](http://effbot.org/zone/element-index.htm) module. + +Installation +==================== + + $ npm install elementtree + +Using the library +==================== + +For the usage refer to the Python ElementTree library documentation - [http://effbot.org/zone/element-index.htm#usage](http://effbot.org/zone/element-index.htm#usage). + +Supported XPath expressions in `find`, `findall` and `findtext` methods are listed on [http://effbot.org/zone/element-xpath.htm](http://effbot.org/zone/element-xpath.htm). + +Example 1 – Creating An XML Document +==================== + +This example shows how to build a valid XML document that can be published to +Atom Hopper. Atom Hopper is used internally as a bridge from products all the +way to collecting revenue, called “Usage.” MaaS and other products send similar +events to it every time user performs an action on a resource +(e.g. creates,updates or deletes). Below is an example of leveraging the API +to create a new XML document. + +```javascript +var et = require('elementtree'); +var XML = et.XML; +var ElementTree = et.ElementTree; +var element = et.Element; +var subElement = et.SubElement; + +var date, root, tenantId, serviceName, eventType, usageId, dataCenter, region, +checks, resourceId, category, startTime, resourceName, etree, xml; + +date = new Date(); + +root = element('entry'); +root.set('xmlns', 'http://www.w3.org/2005/Atom'); + +tenantId = subElement(root, 'TenantId'); +tenantId.text = '12345'; + +serviceName = subElement(root, 'ServiceName'); +serviceName.text = 'MaaS'; + +resourceId = subElement(root, 'ResourceID'); +resourceId.text = 'enAAAA'; + +usageId = subElement(root, 'UsageID'); +usageId.text = '550e8400-e29b-41d4-a716-446655440000'; + +eventType = subElement(root, 'EventType'); +eventType.text = 'create'; + +category = subElement(root, 'category'); +category.set('term', 'monitoring.entity.create'); + +dataCenter = subElement(root, 'DataCenter'); +dataCenter.text = 'global'; + +region = subElement(root, 'Region'); +region.text = 'global'; + +startTime = subElement(root, 'StartTime'); +startTime.text = date; + +resourceName = subElement(root, 'ResourceName'); +resourceName.text = 'entity'; + +etree = new ElementTree(root); +xml = etree.write({'xml_declaration': false}); +console.log(xml); +``` + +As you can see, both et.Element and et.SubElement are factory methods which +return a new instance of Element and SubElement class, respectively. +When you create a new element (tag) you can use set method to set an attribute. +To set the tag value, assign a value to the .text attribute. + +This example would output a document that looks like this: + +```xml + + 12345 + MaaS + enAAAA + 550e8400-e29b-41d4-a716-446655440000 + create + + global + global + Sun Apr 29 2012 16:37:32 GMT-0700 (PDT) + entity + +``` + +Example 2 – Parsing An XML Document +==================== + +This example shows how to parse an XML document and use simple XPath selectors. +For demonstration purposes, we will use the XML document located at +https://gist.github.com/2554343. + +Behind the scenes, node-elementtree uses Isaac’s sax library for parsing XML, +but the library has a concept of “parsers,” which means it’s pretty simple to +add support for a different parser. + +```javascript +var fs = require('fs'); + +var et = require('elementtree'); + +var XML = et.XML; +var ElementTree = et.ElementTree; +var element = et.Element; +var subElement = et.SubElement; + +var data, etree; + +data = fs.readFileSync('document.xml').toString(); +etree = et.parse(data); + +console.log(etree.findall('./entry/TenantId').length); // 2 +console.log(etree.findtext('./entry/ServiceName')); // MaaS +console.log(etree.findall('./entry/category')[0].get('term')); // monitoring.entity.create +console.log(etree.findall('*/category/[@term="monitoring.entity.update"]').length); // 1 +``` + +Build status +==================== + +[![Build Status](https://secure.travis-ci.org/racker/node-elementtree.png)](http://travis-ci.org/racker/node-elementtree) + + +License +==================== + +node-elementtree is distributed under the [Apache license](http://www.apache.org/licenses/LICENSE-2.0.html). diff --git a/node_modules/elementtree/lib/constants.js b/node_modules/elementtree/lib/constants.js new file mode 100644 index 0000000..b057faf --- /dev/null +++ b/node_modules/elementtree/lib/constants.js @@ -0,0 +1,20 @@ +/* + * Copyright 2011 Rackspace + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var DEFAULT_PARSER = 'sax'; + +exports.DEFAULT_PARSER = DEFAULT_PARSER; diff --git a/node_modules/elementtree/lib/elementpath.js b/node_modules/elementtree/lib/elementpath.js new file mode 100644 index 0000000..2e93f47 --- /dev/null +++ b/node_modules/elementtree/lib/elementpath.js @@ -0,0 +1,343 @@ +/** + * Copyright 2011 Rackspace + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var sprintf = require('./sprintf').sprintf; + +var utils = require('./utils'); +var SyntaxError = require('./errors').SyntaxError; + +var _cache = {}; + +var RE = new RegExp( + "(" + + "'[^']*'|\"[^\"]*\"|" + + "::|" + + "//?|" + + "\\.\\.|" + + "\\(\\)|" + + "[/.*:\\[\\]\\(\\)@=])|" + + "((?:\\{[^}]+\\})?[^/\\[\\]\\(\\)@=\\s]+)|" + + "\\s+", 'g' +); + +var xpath_tokenizer = utils.findall.bind(null, RE); + +function prepare_tag(next, token) { + var tag = token[0]; + + function select(context, result) { + var i, len, elem, rv = []; + + for (i = 0, len = result.length; i < len; i++) { + elem = result[i]; + elem._children.forEach(function(e) { + if (e.tag === tag) { + rv.push(e); + } + }); + } + + return rv; + } + + return select; +} + +function prepare_star(next, token) { + function select(context, result) { + var i, len, elem, rv = []; + + for (i = 0, len = result.length; i < len; i++) { + elem = result[i]; + elem._children.forEach(function(e) { + rv.push(e); + }); + } + + return rv; + } + + return select; +} + +function prepare_dot(next, token) { + function select(context, result) { + var i, len, elem, rv = []; + + for (i = 0, len = result.length; i < len; i++) { + elem = result[i]; + rv.push(elem); + } + + return rv; + } + + return select; +} + +function prepare_iter(next, token) { + var tag; + token = next(); + + if (token[1] === '*') { + tag = '*'; + } + else if (!token[1]) { + tag = token[0] || ''; + } + else { + throw new SyntaxError(token); + } + + function select(context, result) { + var i, len, elem, rv = []; + + for (i = 0, len = result.length; i < len; i++) { + elem = result[i]; + elem.iter(tag, function(e) { + if (e !== elem) { + rv.push(e); + } + }); + } + + return rv; + } + + return select; +} + +function prepare_dot_dot(next, token) { + function select(context, result) { + var i, len, elem, rv = [], parent_map = context.parent_map; + + if (!parent_map) { + context.parent_map = parent_map = {}; + + context.root.iter(null, function(p) { + p._children.forEach(function(e) { + parent_map[e] = p; + }); + }); + } + + for (i = 0, len = result.length; i < len; i++) { + elem = result[i]; + + if (parent_map.hasOwnProperty(elem)) { + rv.push(parent_map[elem]); + } + } + + return rv; + } + + return select; +} + + +function prepare_predicate(next, token) { + var tag, key, value, select; + token = next(); + + if (token[1] === '@') { + // attribute + token = next(); + + if (token[1]) { + throw new SyntaxError(token, 'Invalid attribute predicate'); + } + + key = token[0]; + token = next(); + + if (token[1] === ']') { + select = function(context, result) { + var i, len, elem, rv = []; + + for (i = 0, len = result.length; i < len; i++) { + elem = result[i]; + + if (elem.get(key)) { + rv.push(elem); + } + } + + return rv; + }; + } + else if (token[1] === '=') { + value = next()[1]; + + if (value[0] === '"' || value[value.length - 1] === '\'') { + value = value.slice(1, value.length - 1); + } + else { + throw new SyntaxError(token, 'Ivalid comparison target'); + } + + token = next(); + select = function(context, result) { + var i, len, elem, rv = []; + + for (i = 0, len = result.length; i < len; i++) { + elem = result[i]; + + if (elem.get(key) === value) { + rv.push(elem); + } + } + + return rv; + }; + } + + if (token[1] !== ']') { + throw new SyntaxError(token, 'Invalid attribute predicate'); + } + } + else if (!token[1]) { + tag = token[0] || ''; + token = next(); + + if (token[1] !== ']') { + throw new SyntaxError(token, 'Invalid node predicate'); + } + + select = function(context, result) { + var i, len, elem, rv = []; + + for (i = 0, len = result.length; i < len; i++) { + elem = result[i]; + + if (elem.find(tag)) { + rv.push(elem); + } + } + + return rv; + }; + } + else { + throw new SyntaxError(null, 'Invalid predicate'); + } + + return select; +} + + + +var ops = { + "": prepare_tag, + "*": prepare_star, + ".": prepare_dot, + "..": prepare_dot_dot, + "//": prepare_iter, + "[": prepare_predicate, +}; + +function _SelectorContext(root) { + this.parent_map = null; + this.root = root; +} + +function findall(elem, path) { + var selector, result, i, len, token, value, select, context; + + if (_cache.hasOwnProperty(path)) { + selector = _cache[path]; + } + else { + // TODO: Use smarter cache purging approach + if (Object.keys(_cache).length > 100) { + _cache = {}; + } + + if (path.charAt(0) === '/') { + throw new SyntaxError(null, 'Cannot use absolute path on element'); + } + + result = xpath_tokenizer(path); + selector = []; + + function getToken() { + return result.shift(); + } + + token = getToken(); + while (true) { + var c = token[1] || ''; + value = ops[c](getToken, token); + + if (!value) { + throw new SyntaxError(null, sprintf('Invalid path: %s', path)); + } + + selector.push(value); + token = getToken(); + + if (!token) { + break; + } + else if (token[1] === '/') { + token = getToken(); + } + + if (!token) { + break; + } + } + + _cache[path] = selector; + } + + // Execute slector pattern + result = [elem]; + context = new _SelectorContext(elem); + + for (i = 0, len = selector.length; i < len; i++) { + select = selector[i]; + result = select(context, result); + } + + return result || []; +} + +function find(element, path) { + var resultElements = findall(element, path); + + if (resultElements && resultElements.length > 0) { + return resultElements[0]; + } + + return null; +} + +function findtext(element, path, defvalue) { + var resultElements = findall(element, path); + + if (resultElements && resultElements.length > 0) { + return resultElements[0].text; + } + + return defvalue; +} + + +exports.find = find; +exports.findall = findall; +exports.findtext = findtext; diff --git a/node_modules/elementtree/lib/elementtree.js b/node_modules/elementtree/lib/elementtree.js new file mode 100644 index 0000000..61d9276 --- /dev/null +++ b/node_modules/elementtree/lib/elementtree.js @@ -0,0 +1,611 @@ +/** + * Copyright 2011 Rackspace + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var sprintf = require('./sprintf').sprintf; + +var utils = require('./utils'); +var ElementPath = require('./elementpath'); +var TreeBuilder = require('./treebuilder').TreeBuilder; +var get_parser = require('./parser').get_parser; +var constants = require('./constants'); + +var element_ids = 0; + +function Element(tag, attrib) +{ + this._id = element_ids++; + this.tag = tag; + this.attrib = {}; + this.text = null; + this.tail = null; + this._children = []; + + if (attrib) { + this.attrib = utils.merge(this.attrib, attrib); + } +} + +Element.prototype.toString = function() +{ + return sprintf("", this.tag, this._id); +}; + +Element.prototype.makeelement = function(tag, attrib) +{ + return new Element(tag, attrib); +}; + +Element.prototype.len = function() +{ + return this._children.length; +}; + +Element.prototype.getItem = function(index) +{ + return this._children[index]; +}; + +Element.prototype.setItem = function(index, element) +{ + this._children[index] = element; +}; + +Element.prototype.delItem = function(index) +{ + this._children.splice(index, 1); +}; + +Element.prototype.getSlice = function(start, stop) +{ + return this._children.slice(start, stop); +}; + +Element.prototype.setSlice = function(start, stop, elements) +{ + var i; + var k = 0; + for (i = start; i < stop; i++, k++) { + this._children[i] = elements[k]; + } +}; + +Element.prototype.delSlice = function(start, stop) +{ + this._children.splice(start, stop - start); +}; + +Element.prototype.append = function(element) +{ + this._children.push(element); +}; + +Element.prototype.extend = function(elements) +{ + this._children.concat(elements); +}; + +Element.prototype.insert = function(index, element) +{ + this._children[index] = element; +}; + +Element.prototype.remove = function(element) +{ + this._children = this._children.filter(function(e) { + /* TODO: is this the right way to do this? */ + if (e._id === element._id) { + return false; + } + return true; + }); +}; + +Element.prototype.getchildren = function() { + return this._children; +}; + +Element.prototype.find = function(path) +{ + return ElementPath.find(this, path); +}; + +Element.prototype.findtext = function(path, defvalue) +{ + return ElementPath.findtext(this, path, defvalue); +}; + +Element.prototype.findall = function(path, defvalue) +{ + return ElementPath.findall(this, path, defvalue); +}; + +Element.prototype.clear = function() +{ + this.attrib = {}; + this._children = []; + this.text = null; + this.tail = null; +}; + +Element.prototype.get = function(key, defvalue) +{ + if (this.attrib[key] !== undefined) { + return this.attrib[key]; + } + else { + return defvalue; + } +}; + +Element.prototype.set = function(key, value) +{ + this.attrib[key] = value; +}; + +Element.prototype.keys = function() +{ + return Object.keys(this.attrib); +}; + +Element.prototype.items = function() +{ + return utils.items(this.attrib); +}; + +/* + * In python this uses a generator, but in v8 we don't have em, + * so we use a callback instead. + **/ +Element.prototype.iter = function(tag, callback) +{ + var self = this; + var i, child; + + if (tag === "*") { + tag = null; + } + + if (tag === null || this.tag === tag) { + callback(self); + } + + for (i = 0; i < this._children.length; i++) { + child = this._children[i]; + child.iter(tag, function(e) { + callback(e); + }); + } +}; + +Element.prototype.itertext = function(callback) +{ + this.iter(null, function(e) { + if (e.text) { + callback(e.text); + } + + if (e.tail) { + callback(e.tail); + } + }); +}; + + +function SubElement(parent, tag, attrib) { + var element = parent.makeelement(tag, attrib); + parent.append(element); + return element; +} + +function Comment(text) { + var element = new Element(Comment); + if (text) { + element.text = text; + } + return element; +} + +function CData(text) { + var element = new Element(CData); + if (text) { + element.text = text; + } + return element; +} + +function ProcessingInstruction(target, text) +{ + var element = new Element(ProcessingInstruction); + element.text = target; + if (text) { + element.text = element.text + " " + text; + } + return element; +} + +function QName(text_or_uri, tag) +{ + if (tag) { + text_or_uri = sprintf("{%s}%s", text_or_uri, tag); + } + this.text = text_or_uri; +} + +QName.prototype.toString = function() { + return this.text; +}; + +function ElementTree(element) +{ + this._root = element; +} + +ElementTree.prototype.getroot = function() { + return this._root; +}; + +ElementTree.prototype._setroot = function(element) { + this._root = element; +}; + +ElementTree.prototype.parse = function(source, parser) { + if (!parser) { + parser = get_parser(constants.DEFAULT_PARSER); + parser = new parser.XMLParser(new TreeBuilder()); + } + + parser.feed(source); + this._root = parser.close(); + return this._root; +}; + +ElementTree.prototype.iter = function(tag, callback) { + this._root.iter(tag, callback); +}; + +ElementTree.prototype.find = function(path) { + return this._root.find(path); +}; + +ElementTree.prototype.findtext = function(path, defvalue) { + return this._root.findtext(path, defvalue); +}; + +ElementTree.prototype.findall = function(path) { + return this._root.findall(path); +}; + +/** + * Unlike ElementTree, we don't write to a file, we return you a string. + */ +ElementTree.prototype.write = function(options) { + var sb = []; + options = utils.merge({ + encoding: 'utf-8', + xml_declaration: null, + default_namespace: null, + method: 'xml'}, options); + + if (options.xml_declaration !== false) { + sb.push("\n"); + } + + if (options.method === "text") { + _serialize_text(sb, self._root, encoding); + } + else { + var qnames, namespaces, indent, indent_string; + var x = _namespaces(this._root, options.encoding, options.default_namespace); + qnames = x[0]; + namespaces = x[1]; + + if (options.hasOwnProperty('indent')) { + indent = 0; + indent_string = new Array(options.indent + 1).join(' '); + } + else { + indent = false; + } + + if (options.method === "xml") { + _serialize_xml(function(data) { + sb.push(data); + }, this._root, options.encoding, qnames, namespaces, indent, indent_string); + } + else { + /* TODO: html */ + throw new Error("unknown serialization method "+ options.method); + } + } + + return sb.join(""); +}; + +var _namespace_map = { + /* "well-known" namespace prefixes */ + "http://www.w3.org/XML/1998/namespace": "xml", + "http://www.w3.org/1999/xhtml": "html", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf", + "http://schemas.xmlsoap.org/wsdl/": "wsdl", + /* xml schema */ + "http://www.w3.org/2001/XMLSchema": "xs", + "http://www.w3.org/2001/XMLSchema-instance": "xsi", + /* dublic core */ + "http://purl.org/dc/elements/1.1/": "dc", +}; + +function register_namespace(prefix, uri) { + if (/ns\d+$/.test(prefix)) { + throw new Error('Prefix format reserved for internal use'); + } + + if (_namespace_map.hasOwnProperty(uri) && _namespace_map[uri] === prefix) { + delete _namespace_map[uri]; + } + + _namespace_map[uri] = prefix; +} + + +function _escape(text, encoding, isAttribute, isText) { + if (text) { + text = text.toString(); + text = text.replace(/&/g, '&'); + text = text.replace(//g, '>'); + if (!isText) { + text = text.replace(/\n/g, ' '); + text = text.replace(/\r/g, ' '); + } + if (isAttribute) { + text = text.replace(/"/g, '"'); + } + } + return text; +} + +/* TODO: benchmark single regex */ +function _escape_attrib(text, encoding) { + return _escape(text, encoding, true); +} + +function _escape_cdata(text, encoding) { + return _escape(text, encoding, false); +} + +function _escape_text(text, encoding) { + return _escape(text, encoding, false, true); +} + +function _namespaces(elem, encoding, default_namespace) { + var qnames = {}; + var namespaces = {}; + + if (default_namespace) { + namespaces[default_namespace] = ""; + } + + function encode(text) { + return text; + } + + function add_qname(qname) { + if (qname[0] === "{") { + var tmp = qname.substring(1).split("}", 2); + var uri = tmp[0]; + var tag = tmp[1]; + var prefix = namespaces[uri]; + + if (prefix === undefined) { + prefix = _namespace_map[uri]; + if (prefix === undefined) { + prefix = "ns" + Object.keys(namespaces).length; + } + if (prefix !== "xml") { + namespaces[uri] = prefix; + } + } + + if (prefix) { + qnames[qname] = sprintf("%s:%s", prefix, tag); + } + else { + qnames[qname] = tag; + } + } + else { + if (default_namespace) { + throw new Error('cannot use non-qualified names with default_namespace option'); + } + + qnames[qname] = qname; + } + } + + + elem.iter(null, function(e) { + var i; + var tag = e.tag; + var text = e.text; + var items = e.items(); + + if (tag instanceof QName && qnames[tag.text] === undefined) { + add_qname(tag.text); + } + else if (typeof(tag) === "string") { + add_qname(tag); + } + else if (tag !== null && tag !== Comment && tag !== CData && tag !== ProcessingInstruction) { + throw new Error('Invalid tag type for serialization: '+ tag); + } + + if (text instanceof QName && qnames[text.text] === undefined) { + add_qname(text.text); + } + + items.forEach(function(item) { + var key = item[0], + value = item[1]; + if (key instanceof QName) { + key = key.text; + } + + if (qnames[key] === undefined) { + add_qname(key); + } + + if (value instanceof QName && qnames[value.text] === undefined) { + add_qname(value.text); + } + }); + }); + return [qnames, namespaces]; +} + +function _serialize_xml(write, elem, encoding, qnames, namespaces, indent, indent_string) { + var tag = elem.tag; + var text = elem.text; + var items; + var i; + + var newlines = indent || (indent === 0); + write(Array(indent + 1).join(indent_string)); + + if (tag === Comment) { + write(sprintf("", _escape_cdata(text, encoding))); + } + else if (tag === ProcessingInstruction) { + write(sprintf("", _escape_cdata(text, encoding))); + } + else if (tag === CData) { + text = text || ''; + write(sprintf("", text)); + } + else { + tag = qnames[tag]; + if (tag === undefined) { + if (text) { + write(_escape_text(text, encoding)); + } + elem.iter(function(e) { + _serialize_xml(write, e, encoding, qnames, null, newlines ? indent + 1 : false, indent_string); + }); + } + else { + write("<" + tag); + items = elem.items(); + + if (items || namespaces) { + items.sort(); // lexical order + + items.forEach(function(item) { + var k = item[0], + v = item[1]; + + if (k instanceof QName) { + k = k.text; + } + + if (v instanceof QName) { + v = qnames[v.text]; + } + else { + v = _escape_attrib(v, encoding); + } + write(sprintf(" %s=\"%s\"", qnames[k], v)); + }); + + if (namespaces) { + items = utils.items(namespaces); + items.sort(function(a, b) { return a[1] < b[1]; }); + + items.forEach(function(item) { + var k = item[1], + v = item[0]; + + if (k) { + k = ':' + k; + } + + write(sprintf(" xmlns%s=\"%s\"", k, _escape_attrib(v, encoding))); + }); + } + } + + if (text || elem.len()) { + if (text && text.toString().match(/^\s*$/)) { + text = null; + } + + write(">"); + if (!text && newlines) { + write("\n"); + } + + if (text) { + write(_escape_text(text, encoding)); + } + elem._children.forEach(function(e) { + _serialize_xml(write, e, encoding, qnames, null, newlines ? indent + 1 : false, indent_string); + }); + + if (!text && indent) { + write(Array(indent + 1).join(indent_string)); + } + write(""); + } + else { + write(" />"); + } + } + } + + if (newlines) { + write("\n"); + } +} + +function parse(source, parser) { + var tree = new ElementTree(); + tree.parse(source, parser); + return tree; +} + +function tostring(element, options) { + return new ElementTree(element).write(options); +} + +exports.PI = ProcessingInstruction; +exports.Comment = Comment; +exports.CData = CData; +exports.ProcessingInstruction = ProcessingInstruction; +exports.SubElement = SubElement; +exports.QName = QName; +exports.ElementTree = ElementTree; +exports.ElementPath = ElementPath; +exports.Element = function(tag, attrib) { + return new Element(tag, attrib); +}; + +exports.XML = function(data) { + var et = new ElementTree(); + return et.parse(data); +}; + +exports.parse = parse; +exports.register_namespace = register_namespace; +exports.tostring = tostring; diff --git a/node_modules/elementtree/lib/errors.js b/node_modules/elementtree/lib/errors.js new file mode 100644 index 0000000..e8742be --- /dev/null +++ b/node_modules/elementtree/lib/errors.js @@ -0,0 +1,31 @@ +/** + * Copyright 2011 Rackspace + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var util = require('util'); + +var sprintf = require('./sprintf').sprintf; + +function SyntaxError(token, msg) { + msg = msg || sprintf('Syntax Error at token %s', token.toString()); + this.token = token; + this.message = msg; + Error.call(this, msg); +} + +util.inherits(SyntaxError, Error); + +exports.SyntaxError = SyntaxError; diff --git a/node_modules/elementtree/lib/parser.js b/node_modules/elementtree/lib/parser.js new file mode 100644 index 0000000..7307ee4 --- /dev/null +++ b/node_modules/elementtree/lib/parser.js @@ -0,0 +1,33 @@ +/* + * Copyright 2011 Rackspace + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* TODO: support node-expat C++ module optionally */ + +var util = require('util'); +var parsers = require('./parsers/index'); + +function get_parser(name) { + if (name === 'sax') { + return parsers.sax; + } + else { + throw new Error('Invalid parser: ' + name); + } +} + + +exports.get_parser = get_parser; diff --git a/node_modules/elementtree/lib/parsers/index.js b/node_modules/elementtree/lib/parsers/index.js new file mode 100644 index 0000000..5eac5c8 --- /dev/null +++ b/node_modules/elementtree/lib/parsers/index.js @@ -0,0 +1 @@ +exports.sax = require('./sax'); diff --git a/node_modules/elementtree/lib/parsers/sax.js b/node_modules/elementtree/lib/parsers/sax.js new file mode 100644 index 0000000..69b0a59 --- /dev/null +++ b/node_modules/elementtree/lib/parsers/sax.js @@ -0,0 +1,56 @@ +var util = require('util'); + +var sax = require('sax'); + +var TreeBuilder = require('./../treebuilder').TreeBuilder; + +function XMLParser(target) { + this.parser = sax.parser(true); + + this.target = (target) ? target : new TreeBuilder(); + + this.parser.onopentag = this._handleOpenTag.bind(this); + this.parser.ontext = this._handleText.bind(this); + this.parser.oncdata = this._handleCdata.bind(this); + this.parser.ondoctype = this._handleDoctype.bind(this); + this.parser.oncomment = this._handleComment.bind(this); + this.parser.onclosetag = this._handleCloseTag.bind(this); + this.parser.onerror = this._handleError.bind(this); +} + +XMLParser.prototype._handleOpenTag = function(tag) { + this.target.start(tag.name, tag.attributes); +}; + +XMLParser.prototype._handleText = function(text) { + this.target.data(text); +}; + +XMLParser.prototype._handleCdata = function(text) { + this.target.data(text); +}; + +XMLParser.prototype._handleDoctype = function(text) { +}; + +XMLParser.prototype._handleComment = function(comment) { +}; + +XMLParser.prototype._handleCloseTag = function(tag) { + this.target.end(tag); +}; + +XMLParser.prototype._handleError = function(err) { + throw err; +}; + +XMLParser.prototype.feed = function(chunk) { + this.parser.write(chunk); +}; + +XMLParser.prototype.close = function() { + this.parser.close(); + return this.target.close(); +}; + +exports.XMLParser = XMLParser; diff --git a/node_modules/elementtree/lib/sprintf.js b/node_modules/elementtree/lib/sprintf.js new file mode 100644 index 0000000..f802c1b --- /dev/null +++ b/node_modules/elementtree/lib/sprintf.js @@ -0,0 +1,86 @@ +/* + * Copyright 2011 Rackspace + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var cache = {}; + + +// Do any others need escaping? +var TO_ESCAPE = { + '\'': '\\\'', + '\n': '\\n' +}; + + +function populate(formatter) { + var i, type, + key = formatter, + prev = 0, + arg = 1, + builder = 'return \''; + + for (i = 0; i < formatter.length; i++) { + if (formatter[i] === '%') { + type = formatter[i + 1]; + + switch (type) { + case 's': + builder += formatter.slice(prev, i) + '\' + arguments[' + arg + '] + \''; + prev = i + 2; + arg++; + break; + case 'j': + builder += formatter.slice(prev, i) + '\' + JSON.stringify(arguments[' + arg + ']) + \''; + prev = i + 2; + arg++; + break; + case '%': + builder += formatter.slice(prev, i + 1); + prev = i + 2; + i++; + break; + } + + + } else if (TO_ESCAPE[formatter[i]]) { + builder += formatter.slice(prev, i) + TO_ESCAPE[formatter[i]]; + prev = i + 1; + } + } + + builder += formatter.slice(prev) + '\';'; + cache[key] = new Function(builder); +} + + +/** + * A fast version of sprintf(), which currently only supports the %s and %j. + * This caches a formatting function for each format string that is used, so + * you should only use this sprintf() will be called many times with a single + * format string and a limited number of format strings will ever be used (in + * general this means that format strings should be string literals). + * + * @param {String} formatter A format string. + * @param {...String} var_args Values that will be formatted by %s and %j. + * @return {String} The formatted output. + */ +exports.sprintf = function(formatter, var_args) { + if (!cache[formatter]) { + populate(formatter); + } + + return cache[formatter].apply(null, arguments); +}; diff --git a/node_modules/elementtree/lib/treebuilder.js b/node_modules/elementtree/lib/treebuilder.js new file mode 100644 index 0000000..393a98f --- /dev/null +++ b/node_modules/elementtree/lib/treebuilder.js @@ -0,0 +1,60 @@ +function TreeBuilder(element_factory) { + this._data = []; + this._elem = []; + this._last = null; + this._tail = null; + if (!element_factory) { + /* evil circular dep */ + element_factory = require('./elementtree').Element; + } + this._factory = element_factory; +} + +TreeBuilder.prototype.close = function() { + return this._last; +}; + +TreeBuilder.prototype._flush = function() { + if (this._data) { + if (this._last !== null) { + var text = this._data.join(""); + if (this._tail) { + this._last.tail = text; + } + else { + this._last.text = text; + } + } + this._data = []; + } +}; + +TreeBuilder.prototype.data = function(data) { + this._data.push(data); +}; + +TreeBuilder.prototype.start = function(tag, attrs) { + this._flush(); + var elem = this._factory(tag, attrs); + this._last = elem; + + if (this._elem.length) { + this._elem[this._elem.length - 1].append(elem); + } + + this._elem.push(elem); + + this._tail = null; +}; + +TreeBuilder.prototype.end = function(tag) { + this._flush(); + this._last = this._elem.pop(); + if (this._last.tag !== tag) { + throw new Error("end tag mismatch"); + } + this._tail = 1; + return this._last; +}; + +exports.TreeBuilder = TreeBuilder; diff --git a/node_modules/elementtree/lib/utils.js b/node_modules/elementtree/lib/utils.js new file mode 100644 index 0000000..b08a670 --- /dev/null +++ b/node_modules/elementtree/lib/utils.js @@ -0,0 +1,72 @@ +/** + * Copyright 2011 Rackspace + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * @param {Object} hash. + * @param {Array} ignored. + */ +function items(hash, ignored) { + ignored = ignored || null; + var k, rv = []; + + function is_ignored(key) { + if (!ignored || ignored.length === 0) { + return false; + } + + return ignored.indexOf(key); + } + + for (k in hash) { + if (hash.hasOwnProperty(k) && !(is_ignored(ignored))) { + rv.push([k, hash[k]]); + } + } + + return rv; +} + + +function findall(re, str) { + var match, matches = []; + + while ((match = re.exec(str))) { + matches.push(match); + } + + return matches; +} + +function merge(a, b) { + var c = {}, attrname; + + for (attrname in a) { + if (a.hasOwnProperty(attrname)) { + c[attrname] = a[attrname]; + } + } + for (attrname in b) { + if (b.hasOwnProperty(attrname)) { + c[attrname] = b[attrname]; + } + } + return c; +} + +exports.items = items; +exports.findall = findall; +exports.merge = merge; diff --git a/node_modules/elementtree/package.json b/node_modules/elementtree/package.json new file mode 100644 index 0000000..e7480e7 --- /dev/null +++ b/node_modules/elementtree/package.json @@ -0,0 +1,39 @@ +{ + "author": "Rackspace US, Inc.", + "contributors": [ + "Paul Querna ", + "Tomaz Muraus " + ], + "name": "elementtree", + "description": "XML Serialization and Parsing module based on Python's ElementTree.", + "version": "0.1.7", + "keywords": [ + "xml", + "sax", + "parser", + "seralization", + "elementtree" + ], + "homepage": "https://github.com/racker/node-elementtree", + "repository": { + "type": "git", + "url": "git://github.com/racker/node-elementtree.git" + }, + "main": "lib/elementtree.js", + "directories": { + "lib": "lib" + }, + "scripts": { + "test": "make test" + }, + "engines": { + "node": ">= 0.4.0" + }, + "dependencies": { + "sax": "1.1.4" + }, + "devDependencies": { + "whiskey": "0.8.x" + }, + "license": "Apache-2.0" +} diff --git a/node_modules/elementtree/tests/data/bom-xml.xml b/node_modules/elementtree/tests/data/bom-xml.xml new file mode 100644 index 0000000..122cce6 --- /dev/null +++ b/node_modules/elementtree/tests/data/bom-xml.xml @@ -0,0 +1,17 @@ + + + dd + test_object_1 + 4281c348eaf83e70ddce0e07221c3d28 + 14 + application/octetstream + 2009-02-03T05:26:32.612278 + + + test_object_2 + b039efe731ad111bc1b0ef221c3849d0 + 64 + application/octetstream + 2009-02-03T05:26:32.612278 + + diff --git a/node_modules/elementtree/tests/data/xml1.xml b/node_modules/elementtree/tests/data/xml1.xml new file mode 100644 index 0000000..72c33ae --- /dev/null +++ b/node_modules/elementtree/tests/data/xml1.xml @@ -0,0 +1,17 @@ + + + dd + test_object_1 + 4281c348eaf83e70ddce0e07221c3d28 + 14 + application/octetstream + 2009-02-03T05:26:32.612278 + + + test_object_2 + b039efe731ad111bc1b0ef221c3849d0 + 64 + application/octetstream + 2009-02-03T05:26:32.612278 + + diff --git a/node_modules/elementtree/tests/data/xml2.xml b/node_modules/elementtree/tests/data/xml2.xml new file mode 100644 index 0000000..5f94bbd --- /dev/null +++ b/node_modules/elementtree/tests/data/xml2.xml @@ -0,0 +1,14 @@ + + + + Hello World + + + + + + + + diff --git a/node_modules/elementtree/tests/test-simple.js b/node_modules/elementtree/tests/test-simple.js new file mode 100644 index 0000000..629a208 --- /dev/null +++ b/node_modules/elementtree/tests/test-simple.js @@ -0,0 +1,348 @@ +/** + * Copyright 2011 Rackspace + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var fs = require('fs'); +var path = require('path'); + +var sprintf = require('./../lib/sprintf').sprintf; +var et = require('elementtree'); +var XML = et.XML; +var ElementTree = et.ElementTree; +var Element = et.Element; +var SubElement = et.SubElement; +var SyntaxError = require('./../lib/errors').SyntaxError; + +function readFile(name) { + return fs.readFileSync(path.join(__dirname, '/data/', name), 'utf8'); +} + +exports['test_simplest'] = function(test, assert) { + /* Ported from */ + var Element = et.Element; + var root = Element('root'); + root.append(Element('one')); + root.append(Element('two')); + root.append(Element('three')); + assert.equal(3, root.len()); + assert.equal('one', root.getItem(0).tag); + assert.equal('two', root.getItem(1).tag); + assert.equal('three', root.getItem(2).tag); + test.finish(); +}; + + +exports['test_attribute_values'] = function(test, assert) { + var XML = et.XML; + var root = XML(''); + assert.equal('Alpha', root.attrib['alpha']); + assert.equal('Beta', root.attrib['beta']); + assert.equal('Gamma', root.attrib['gamma']); + test.finish(); +}; + + +exports['test_findall'] = function(test, assert) { + var XML = et.XML; + var root = XML(''); + + assert.equal(root.findall("c").length, 1); + assert.equal(root.findall(".//c").length, 2); + assert.equal(root.findall(".//b").length, 3); + assert.equal(root.findall(".//b")[0]._children.length, 1); + assert.equal(root.findall(".//b")[1]._children.length, 0); + assert.equal(root.findall(".//b")[2]._children.length, 0); + assert.deepEqual(root.findall('.//b')[0], root.getchildren()[0]); + + test.finish(); +}; + +exports['test_find'] = function(test, assert) { + var a = Element('a'); + var b = SubElement(a, 'b'); + var c = SubElement(a, 'c'); + + assert.deepEqual(a.find('./b/..'), a); + test.finish(); +}; + +exports['test_elementtree_find_qname'] = function(test, assert) { + var tree = new et.ElementTree(XML('')); + assert.deepEqual(tree.find(new et.QName('c')), tree.getroot()._children[2]); + test.finish(); +}; + +exports['test_attrib_ns_clear'] = function(test, assert) { + var attribNS = '{http://foo/bar}x'; + + var par = Element('par'); + par.set(attribNS, 'a'); + var child = SubElement(par, 'child'); + child.set(attribNS, 'b'); + + assert.equal('a', par.get(attribNS)); + assert.equal('b', child.get(attribNS)); + + par.clear(); + assert.equal(null, par.get(attribNS)); + assert.equal('b', child.get(attribNS)); + test.finish(); +}; + +exports['test_create_tree_and_parse_simple'] = function(test, assert) { + var i = 0; + var e = new Element('bar', {}); + var expected = "\n" + + 'ponies'; + + SubElement(e, "blah", {a: 11}); + SubElement(e, "blah", {a: 12}); + var se = et.SubElement(e, "gag", {a: '13', b: 'abc'}); + se.text = 'ponies'; + + se.itertext(function(text) { + assert.equal(text, 'ponies'); + i++; + }); + + assert.equal(i, 1); + var etree = new ElementTree(e); + var xml = etree.write(); + assert.equal(xml, expected); + test.finish(); +}; + +exports['test_write_with_options'] = function(test, assert) { + var i = 0; + var e = new Element('bar', {}); + var expected1 = "\n" + + '\n' + + ' \n' + + ' test\n' + + ' \n' + + ' \n' + + ' ponies\n' + + '\n'; + var expected2 = "\n" + + '\n' + + ' \n' + + ' test\n' + + ' \n' + + ' \n' + + ' ponies\n' + + '\n'; + + var expected3 = "\n" + + '\n' + + ' \n' + + ' Hello World\n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' Test & Test & Test\n' + + ' \n' + + '\n'; + + var se1 = SubElement(e, "blah", {a: 11}); + var se2 = SubElement(se1, "baz", {d: 11}); + se2.text = 'test'; + SubElement(e, "blah", {a: 12}); + var se = et.SubElement(e, "gag", {a: '13', b: 'abc'}); + se.text = 'ponies'; + + se.itertext(function(text) { + assert.equal(text, 'ponies'); + i++; + }); + + assert.equal(i, 1); + var etree = new ElementTree(e); + var xml1 = etree.write({'indent': 4}); + var xml2 = etree.write({'indent': 2}); + assert.equal(xml1, expected1); + assert.equal(xml2, expected2); + + var file = readFile('xml2.xml'); + var etree2 = et.parse(file); + var xml3 = etree2.write({'indent': 4}); + assert.equal(xml3, expected3); + test.finish(); +}; + +exports['test_parse_and_find_2'] = function(test, assert) { + var data = readFile('xml1.xml'); + var etree = et.parse(data); + + assert.equal(etree.findall('./object').length, 2); + assert.equal(etree.findall('[@name]').length, 1); + assert.equal(etree.findall('[@name="test_container_1"]').length, 1); + assert.equal(etree.findall('[@name=\'test_container_1\']').length, 1); + assert.equal(etree.findall('./object')[0].findtext('name'), 'test_object_1'); + assert.equal(etree.findtext('./object/name'), 'test_object_1'); + assert.equal(etree.findall('.//bytes').length, 2); + assert.equal(etree.findall('*/bytes').length, 2); + assert.equal(etree.findall('*/foobar').length, 0); + + test.finish(); +}; + +exports['test_namespaced_attribute'] = function(test, assert) { + var data = readFile('xml1.xml'); + var etree = et.parse(data); + + assert.equal(etree.findall('*/bytes[@android:type="cool"]').length, 1); + + test.finish(); +} + +exports['test_syntax_errors'] = function(test, assert) { + var expressions = [ './/@bar', '[@bar', '[@foo=bar]', '[@', '/bar' ]; + var errCount = 0; + var data = readFile('xml1.xml'); + var etree = et.parse(data); + + expressions.forEach(function(expression) { + try { + etree.findall(expression); + } + catch (err) { + errCount++; + } + }); + + assert.equal(errCount, expressions.length); + test.finish(); +}; + +exports['test_register_namespace'] = function(test, assert){ + var prefix = 'TESTPREFIX'; + var namespace = 'http://seriously.unknown/namespace/URI'; + var errCount = 0; + + var etree = Element(sprintf('{%s}test', namespace)); + assert.equal(et.tostring(etree, { 'xml_declaration': false}), + sprintf('', namespace)); + + et.register_namespace(prefix, namespace); + var etree = Element(sprintf('{%s}test', namespace)); + assert.equal(et.tostring(etree, { 'xml_declaration': false}), + sprintf('<%s:test xmlns:%s="%s" />', prefix, prefix, namespace)); + + try { + et.register_namespace('ns25', namespace); + } + catch (err) { + errCount++; + } + + assert.equal(errCount, 1, 'Reserved prefix used, but exception was not thrown'); + test.finish(); +}; + +exports['test_tostring'] = function(test, assert) { + var a = Element('a'); + var b = SubElement(a, 'b'); + var c = SubElement(a, 'c'); + c.text = 543; + + assert.equal(et.tostring(a, { 'xml_declaration': false }), '543'); + assert.equal(et.tostring(c, { 'xml_declaration': false }), '543'); + test.finish(); +}; + +exports['test_escape'] = function(test, assert) { + var a = Element('a'); + var b = SubElement(a, 'b'); + b.text = '&&&&<>"\n\r'; + + assert.equal(et.tostring(a, { 'xml_declaration': false }), '&&&&<>\"\n\r'); + test.finish(); +}; + +exports['test_find_null'] = function(test, assert) { + var root = Element('root'); + var node = SubElement(root, 'node'); + var leaf = SubElement(node, 'leaf'); + leaf.text = 'ipsum'; + + assert.equal(root.find('node/leaf'), leaf); + assert.equal(root.find('no-such-node/leaf'), null); + test.finish(); +}; + +exports['test_findtext_null'] = function(test, assert) { + var root = Element('root'); + var node = SubElement(root, 'node'); + var leaf = SubElement(node, 'leaf'); + leaf.text = 'ipsum'; + + assert.equal(root.findtext('node/leaf'), 'ipsum'); + assert.equal(root.findtext('no-such-node/leaf'), null); + test.finish(); +}; + +exports['test_remove'] = function(test, assert) { + var root = Element('root'); + var node1 = SubElement(root, 'node1'); + var node2 = SubElement(root, 'node2'); + var node3 = SubElement(root, 'node3'); + + assert.equal(root.len(), 3); + + root.remove(node2); + + assert.equal(root.len(), 2); + assert.equal(root.getItem(0).tag, 'node1') + assert.equal(root.getItem(1).tag, 'node3') + + test.finish(); +}; + +exports['test_cdata_write'] = function(test, assert) { + var root, etree, xml, values, value, i; + + values = [ + 'if(0>1) then true;', + 'ponies hello', + '' + ]; + + for (i = 0; i < values.length; i++) { + value = values[i]; + + root = Element('root'); + root.append(et.CData(value)); + etree = new ElementTree(root); + xml = etree.write({'xml_declaration': false}); + + assert.equal(xml, sprintf('', value)); + } + + test.finish(); +}; + +exports['test_read_bom'] = function(test, assert) { + var file = readFile('bom-xml.xml'); + var etree = et.parse(file); + + // If parse finished, test was successful + + test.finish(); +}; diff --git a/node_modules/endent/.travis.yml b/node_modules/endent/.travis.yml new file mode 100644 index 0000000..e32a325 --- /dev/null +++ b/node_modules/endent/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - "node" + - "8" diff --git a/node_modules/endent/LICENSE b/node_modules/endent/LICENSE new file mode 100644 index 0000000..8333b51 --- /dev/null +++ b/node_modules/endent/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Zhou Hancheng (z308114274@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/endent/README.md b/node_modules/endent/README.md new file mode 100644 index 0000000..5a66907 --- /dev/null +++ b/node_modules/endent/README.md @@ -0,0 +1,132 @@ +# Endent [![stability][stability-image]][stability-index] +[![npm][npm-image]][npm-url] +[![travis][travis-image]][travis-url] +[![dm][dm-image]][npm-url] +[![js-standard-style][code-style]][standard] + +[stability-image]: https://img.shields.io/badge/stability-stable-green.svg +[stability-index]: https://nodejs.org/api/documentation.html#documentation_stability_index +[npm-image]: https://img.shields.io/npm/v/endent.svg?style=flat-square +[npm-url]: https://www.npmjs.com/package/endent +[travis-image]: https://img.shields.io/travis/indentjs/endent.svg?style=flat-square +[travis-url]: https://travis-ci.org/indentjs/endent +[dm-image]: http://img.shields.io/npm/dm/endent.svg?style=flat-square +[code-style]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square +[standard]: https://github.com/feross/standard + +An ES6 string tag that makes indentation right, adds some key features to [dedent](https://github.com/dmnd/dedent). + +## Feature + +### pretty object + +```js +import dedent from "dedent" +import endent from "endent" + +var someobj = { + contact: { + jack: 123456, + tom: 654321 + }, + color: "blue" +} + +var somejson = '["bear", "fish", "dog", "cat"]' + +var awfulTmpl = dedent` + function store (state, emitter) { + state["someobj"] = ${JSON.stringify(someobj, null, 2)} + state["somejson"] = ${JSON.stringify(JSON.parse(somejson), null, 2)} + } +` +// use endent.pretty(value) when value is object or array. +var prettyTmpl = endent` + function store (state, emitter) { + state["someobj"] = ${endent.pretty(someobj)} + state["somejson"] = ${somejson} + } +` + +console.log(awfulTmpl + "\n\n" + prettyTmpl) +``` + +```js +// awfulTmpl +function store (state, emitter) { + state["someobj"] = { +"contact": { + "jack": 123456, + "tom": 654321 +}, +"color": "blue" +} + state["somejson"] = [ +"bear", +"fish", +"dog" +] +} + +// prettyTmpl +function store (state, emitter) { + state["someobj"] = { + "contact": { + "jack": 123456, + "tom": 654321 + }, + "color": "blue" + } + state["somejson"] = [ + "bear", + "fish", + "dog", + "cat" + ] +} +``` + +### endows suitable indentation for multiline interpolation + +```js +var dependencies = ['jquery', 'underscore', 'bootstrap'] +var dependencyTmpl = `` +dependencies.forEach((d, i) => { + dependencyTmpl += `var ${d} = require("${d}")\n` +}) + +var awfulTmpl = dedent` + ;(function () { + ${dependencyTmpl} + })() +` + +var prettyTmpl = endent` + ;(function () { + ${dependencyTmpl} + })() +` + +console.log(awfulTmpl + "\n\n" + prettyTmpl) +``` + +```js +// awfulTmpl +;(function () { + var jquery = require('jquery') +var underscore = require('underscore') +var bootstrap = require('bootstrap') +})() + + +// prettyTmpl +;(function () { + var jquery = require('jquery') + var underscore = require('underscore') + var bootstrap = require('bootstrap') +})() + +``` +## License + +MIT diff --git a/node_modules/endent/endent.js b/node_modules/endent/endent.js new file mode 100644 index 0000000..f21f814 --- /dev/null +++ b/node_modules/endent/endent.js @@ -0,0 +1,66 @@ +const dedent = require('dedent') +const objectorarray = require('objectorarray') +const parse = require('fast-json-parse') + +module.exports = endent + +const ENDENT_ID = 'twhZNwxI1aFG3r4' + +function endent (strings, ...values) { + strings = [].concat(strings) + + let result = '' + + for (let i = 0; i < strings.length; i++) { + result += strings[i] + + if (i < values.length) { + let value = values[i] + let isJson = false + + if (parse(value).value) { + value = parse(value).value + isJson = true + } + + if ((value && value[ENDENT_ID]) || isJson) { + let rawlines = result.split('\n') + let l = rawlines[rawlines.length - 1].search(/\S/) + let endentation = l > 0 ? ' '.repeat(l) : '' + let valueJson = isJson ? JSON.stringify(value, null, 2) : value[ENDENT_ID] + let valueLines = valueJson.split('\n') + + valueLines.forEach((l, index) => { + if (index > 0) { + result += ('\n' + endentation + l) + } else { + result += l + } + }) + } else if (typeof value === 'string' && value.includes('\n')) { + let endentations = result.match(/(?:^|\n)( *)$/) + + if (typeof value === 'string') { + let endentation = endentations ? endentations[1] : '' + result += value + .split('\n') + .map((str, i) => { + str = (ENDENT_ID + str) + return i === 0 ? str : `${endentation}${str}` + }) + .join('\n') + } else { + result += value + } + } else { + result += value + } + } + } + result = dedent(result) + return result.split(ENDENT_ID).join('') +} + +endent.pretty = (data) => { + return objectorarray(data) ? {[ENDENT_ID]: JSON.stringify(data, null, 2)} : data +} diff --git a/node_modules/endent/package.json b/node_modules/endent/package.json new file mode 100644 index 0000000..d235ae6 --- /dev/null +++ b/node_modules/endent/package.json @@ -0,0 +1,36 @@ +{ + "name": "endent", + "version": "1.4.1", + "description": "➡️ An ES6 string tag that makes indentation right", + "main": "endent.js", + "scripts": { + "test": "standard && tape test.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/ZhouHansen/endent.git" + }, + "author": { + "name": "zhouhancheng", + "email": "z308114274@gmail.com" + }, + "keywords": [ + "dedent", + "tag", + "multi-line string" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/ZhouHansen/endent/issues" + }, + "homepage": "https://github.com/ZhouHansen/endent#readme", + "dependencies": { + "dedent": "^0.7.0", + "fast-json-parse": "^1.0.3", + "objectorarray": "^1.0.4" + }, + "devDependencies": { + "standard": "^11.0.1", + "tape": "^4.8.0" + } +} diff --git a/node_modules/endent/test.js b/node_modules/endent/test.js new file mode 100644 index 0000000..daae2e0 --- /dev/null +++ b/node_modules/endent/test.js @@ -0,0 +1,137 @@ +var test = require('tape') +var endent = require('./') + +test('object', t => { + t.plan(1) + + var json = JSON.stringify(JSON.parse('[ "abc" ]'), null, 2) + + var someobj = { + contact: { + jack: '123456', + tom: '654321' + }, + name: 'template', + color: 'blue', + animals: ['bear', 'fish', 'dog', 'cat'] + } + + var colors = ['red', 'pink', 'white'] + var objectName = 'someobj' + + var dependencies = ['jquery', 'underscore', 'bootstrap'] + var dependencyTmpl = `` + + dependencies.forEach((d, i) => { + dependencyTmpl += `var ${d} = require("${d}")\n` + }) + + var jsFile = endent` + ${dependencyTmpl} + module.exports = store + + function store (state, emitter) { + emitter.on("DOMContentLoaded", function () { + state["json"] = ${json} + state["${objectName}"] = ${endent.pretty(someobj)} + state["colors"] = ${endent.pretty(colors)} + state["name"] = "${endent.pretty('jack')}" + state["name2"] = "${'tom'}" + state["number"] = ${endent.pretty(123)} + state["number2"] = ${123} + state["Iamundefined"] = ${endent.pretty()} + state["Iamnull"] = ${endent.pretty(null)} + state["Iamregexp"] = ${endent.pretty(/abc/)} + }) + } + ` + + t.equal(jsFile, `var jquery = require("jquery") +var underscore = require("underscore") +var bootstrap = require("bootstrap") + +module.exports = store + +function store (state, emitter) { + emitter.on("DOMContentLoaded", function () { + state["json"] = [ + "abc" + ] + state["someobj"] = { + "contact": { + "jack": "123456", + "tom": "654321" + }, + "name": "template", + "color": "blue", + "animals": [ + "bear", + "fish", + "dog", + "cat" + ] + } + state["colors"] = [ + "red", + "pink", + "white" + ] + state["name"] = "jack" + state["name2"] = "tom" + state["number"] = 123 + state["number2"] = 123 + state["Iamundefined"] = undefined + state["Iamnull"] = null + state["Iamregexp"] = /abc/ + }) +}`) +}) + +test('string', t => { + t.plan(1) + const a = ` +hello + world` + const b = endent` + foo. + ${a} + bar.` + t.equal(b, `foo. + +hello + world +bar.`) +}) + +test('issue#1', t => { + t.plan(1) + const a = '"test"' + const r = endent` + { + ${a}: null + } + ` + t.equal(r, `{ + "test": null +}`) +}) + +test('issue#2', t => { + t.plan(1) + + const r = endent` + foo. + x=${'hello\n world'} + bar. + ` + console.log(r) + t.equal(r, `foo. +x=hello + world +bar.`) +}) + +test('tab', t => { + t.plan(1) + t.equal(endent`foo\tbar`, 'foo\tbar') +}) diff --git a/node_modules/fast-json-parse/.npmignore b/node_modules/fast-json-parse/.npmignore new file mode 100644 index 0000000..e920c16 --- /dev/null +++ b/node_modules/fast-json-parse/.npmignore @@ -0,0 +1,33 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history diff --git a/node_modules/fast-json-parse/.travis.yml b/node_modules/fast-json-parse/.travis.yml new file mode 100644 index 0000000..4a95fb3 --- /dev/null +++ b/node_modules/fast-json-parse/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +sudo: false + +node_js: + - "5" + - "4" + - "0.12" + - "0.11" + - "0.10" diff --git a/node_modules/fast-json-parse/LICENSE b/node_modules/fast-json-parse/LICENSE new file mode 100644 index 0000000..ecf6245 --- /dev/null +++ b/node_modules/fast-json-parse/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Matteo Collina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/fast-json-parse/README.md b/node_modules/fast-json-parse/README.md new file mode 100644 index 0000000..0e0cf73 --- /dev/null +++ b/node_modules/fast-json-parse/README.md @@ -0,0 +1,62 @@ +# fast-json-parse + +[![Build Status](https://travis-ci.org/mcollina/fast-json-parse.svg)](https://travis-ci.org/mcollina/fast-json-parse) + +It is equivalent to [json-parse-safe](http://npm.im/json-parse-safe), +but it set both the `err` and `value` property to null. + +The reason why this is fast is that `try/catch` inhibits the functions +in which you use them to be optimized. This assumption holds true up to +Node 6, from Node 7 and forward this module is not useful anymore. + +## Install + +``` +npm i fast-json-parse --save +``` + +## Usage + +You can use it as a function or via a contructor, as you prefer. + +### function + +```js +'use strict' + +var parse = require('fast-json-parse') +var fs = require('fs') + +var result = parse(fs.readFileSync('./package.json')) + +if (result.err) { + console.log('unable to parse json', result.err.message) +} else { + console.log('json parsed successfully', result.value) +} +``` + +### constructor + +```js +'use strict' + +var Parse = require('fast-json-parse') +var fs = require('fs') + +var result = new Parse(fs.readFileSync('./package.json')) + +if (result.err) { + console.log('unable to parse json', result.err.message) +} else { + console.log('json parsed successfully', result.value) +} +``` + +## Acknowledgements + +fast-json-parse is sponsored by [nearForm](http://nearform.com). + +## License + +MIT diff --git a/node_modules/fast-json-parse/benchmark.js b/node_modules/fast-json-parse/benchmark.js new file mode 100644 index 0000000..7782cc3 --- /dev/null +++ b/node_modules/fast-json-parse/benchmark.js @@ -0,0 +1,85 @@ +'use strict' + +var benchmark = require('benchmark') +var suite = new benchmark.Suite() +var fs = require('fs') +var myData = fs.readFileSync('./package.json') +var Parse = require('./') +var safe = require('json-parse-safe') +var max = 100 + +suite.add('Parse class', function parseBench () { + var parsed = new Parse(myData) + if (parsed.err) { + return + } + + // add a loop to simulate some activity here + for (var i = 0; i < max; i++) { + var count = 0 + var keys = Object.keys(parsed.value) + for (var k = 0; k < keys.length; k++) { + count++ + } + } +}) + +suite.add('Parse wrapped', function parseBench () { + var parsed = Parse(myData) + if (parsed.err) { + return + } + + // add a loop to simulate some activity here + for (var i = 0; i < max; i++) { + var count = 0 + var keys = Object.keys(parsed.value) + for (var k = 0; k < keys.length; k++) { + count++ + } + } +}) + +suite.add('json-parse-safe', function parseBench () { + var parsed = safe(myData) + if (parsed.error) { + return + } + + // add a loop to simulate some activity here + for (var i = 0; i < max; i++) { + var count = 0 + var keys = Object.keys(parsed.value) + for (var k = 0; k < keys.length; k++) { + count++ + } + } +}) + +suite.add('try catch here', function tryCatchBench () { + var data = null + try { + data = JSON.parse(myData) + } catch (err) { + return + } + + // add a loop to simulate some activity here + for (var i = 0; i < max; i++) { + var count = 0 + var keys = Object.keys(data) + for (var k = 0; k < keys.length; k++) { + count++ + } + } +}) + +suite.on('complete', function print () { + for (var i = 0; i < this.length; i++) { + console.log(this[i].toString()) + } + + console.log('Fastest is', this.filter('fastest').map('name')[0]) +}) + +suite.run() diff --git a/node_modules/fast-json-parse/package.json b/node_modules/fast-json-parse/package.json new file mode 100644 index 0000000..e3180ba --- /dev/null +++ b/node_modules/fast-json-parse/package.json @@ -0,0 +1,33 @@ +{ + "name": "fast-json-parse", + "version": "1.0.3", + "description": "Parse json safely and at max speed", + "main": "parse.js", + "repository": { + "type": "git", + "url": "git+https://github.com/mcollina/fast-json-parse.git" + }, + "bugs": { + "url": "https://github.com/mcollina/fast-json-parse/issues" + }, + "homepage": "https://github.com/mcollina/fast-json-parse#readme", + "scripts": { + "test": "standard && tap test.js" + }, + "pre-commit": "test", + "keywords": [ + "parse", + "json", + "fast", + "safe" + ], + "author": "Matteo Collina ", + "license": "MIT", + "devDependencies": { + "benchmark": "^2.1.0", + "json-parse-safe": "^1.0.3", + "pre-commit": "^1.1.2", + "standard": "^6.0.5", + "tap": "^5.5.0" + } +} diff --git a/node_modules/fast-json-parse/parse.js b/node_modules/fast-json-parse/parse.js new file mode 100644 index 0000000..84df99a --- /dev/null +++ b/node_modules/fast-json-parse/parse.js @@ -0,0 +1,16 @@ +'use strict' + +function Parse (data) { + if (!(this instanceof Parse)) { + return new Parse(data) + } + this.err = null + this.value = null + try { + this.value = JSON.parse(data) + } catch (err) { + this.err = err + } +} + +module.exports = Parse diff --git a/node_modules/fast-json-parse/test.js b/node_modules/fast-json-parse/test.js new file mode 100644 index 0000000..571787e --- /dev/null +++ b/node_modules/fast-json-parse/test.js @@ -0,0 +1,46 @@ +'use strict' + +var test = require('tap').test +var Parse = require('./') + +function successTests (value) { + test('parse successfully with the function', function (t) { + t.plan(2) + var result = Parse(value) + t.error(result.err) + t.deepEqual(result.value, JSON.parse(value)) + }) + + test('parse successfully with the constructor', function (t) { + t.plan(2) + var result = new Parse(value) + t.error(result.err) + t.deepEqual(result.value, JSON.parse(value)) + }) +} + +function failureTests (value) { + var expectedErr + try { + JSON.parse(value) + } catch (err) { + expectedErr = err + } + + test('parse unsuccessfully with the function', function (t) { + t.plan(2) + var result = Parse(value) + t.notOk(result.value, 'no value') + t.equal(result.err.message, expectedErr.message) + }) + + test('parse successfully with the constructor', function (t) { + t.plan(2) + var result = new Parse(value) + t.notOk(result.value, 'no value') + t.equal(result.err.message, expectedErr.message) + }) +} + +successTests('{ "object": 32 }') +failureTests('{ "object": 32') diff --git a/node_modules/fs-extra/CHANGELOG.md b/node_modules/fs-extra/CHANGELOG.md new file mode 100644 index 0000000..3049b45 --- /dev/null +++ b/node_modules/fs-extra/CHANGELOG.md @@ -0,0 +1,864 @@ +8.1.0 / 2019-06-28 +------------------ + +- Add support for promisified `fs.realpath.native` in Node v9.2+ ([#650](https://github.com/jprichardson/node-fs-extra/issues/650), [#682](https://github.com/jprichardson/node-fs-extra/pull/682)) +- Update `graceful-fs` dependency ([#700](https://github.com/jprichardson/node-fs-extra/pull/700)) +- Use `graceful-fs` everywhere ([#700](https://github.com/jprichardson/node-fs-extra/pull/700)) + +8.0.1 / 2019-05-13 +------------------ + +- Fix bug `Maximum call stack size exceeded` error in `util/stat` ([#679](https://github.com/jprichardson/node-fs-extra/pull/679)) + +8.0.0 / 2019-05-11 +------------------ + +**NOTE:** Node.js v6 support is deprecated, and will be dropped in the next major release. + +- Use `renameSync()` under the hood in `moveSync()` +- Fix bug with bind-mounted directories in `copy*()` ([#613](https://github.com/jprichardson/node-fs-extra/issues/613), [#618](https://github.com/jprichardson/node-fs-extra/pull/618)) +- Fix bug in `move()` with case-insensitive file systems +- Use `fs.stat()`'s `bigint` option in `copy*()` & `move*()` where possible ([#657](https://github.com/jprichardson/node-fs-extra/issues/657)) + +7.0.1 / 2018-11-07 +------------------ + +- Fix `removeSync()` on Windows, in some cases, it would error out with `ENOTEMPTY` ([#646](https://github.com/jprichardson/node-fs-extra/pull/646)) +- Document `mode` option for `ensureDir*()` ([#587](https://github.com/jprichardson/node-fs-extra/pull/587)) +- Don't include documentation files in npm package tarball ([#642](https://github.com/jprichardson/node-fs-extra/issues/642), [#643](https://github.com/jprichardson/node-fs-extra/pull/643)) + +7.0.0 / 2018-07-16 +------------------ + +- **BREAKING:** Refine `copy*()` handling of symlinks to properly detect symlinks that point to the same file. ([#582](https://github.com/jprichardson/node-fs-extra/pull/582)) +- Fix bug with copying write-protected directories ([#600](https://github.com/jprichardson/node-fs-extra/pull/600)) +- Universalify `fs.lchmod()` ([#596](https://github.com/jprichardson/node-fs-extra/pull/596)) +- Add `engines` field to `package.json` ([#580](https://github.com/jprichardson/node-fs-extra/pull/580)) + +6.0.1 / 2018-05-09 +------------------ + +- Fix `fs.promises` `ExperimentalWarning` on Node v10.1.0 ([#578](https://github.com/jprichardson/node-fs-extra/pull/578)) + +6.0.0 / 2018-05-01 +------------------ + +- Drop support for Node.js versions 4, 5, & 7 ([#564](https://github.com/jprichardson/node-fs-extra/pull/564)) +- Rewrite `move` to use `fs.rename` where possible ([#549](https://github.com/jprichardson/node-fs-extra/pull/549)) +- Don't convert relative paths to absolute paths for `filter` ([#554](https://github.com/jprichardson/node-fs-extra/pull/554)) +- `copy*`'s behavior when `preserveTimestamps` is `false` has been OS-dependent since 5.0.0, but that's now explicitly noted in the docs ([#563](https://github.com/jprichardson/node-fs-extra/pull/563)) +- Fix subdirectory detection for `copy*` & `move*` ([#541](https://github.com/jprichardson/node-fs-extra/pull/541)) +- Handle case-insensitive paths correctly in `copy*` ([#568](https://github.com/jprichardson/node-fs-extra/pull/568)) + +5.0.0 / 2017-12-11 +------------------ + +Significant refactor of `copy()` & `copySync()`, including breaking changes. No changes to other functions in this release. + +Huge thanks to **[@manidlou](https://github.com/manidlou)** for doing most of the work on this release. + +- The `filter` option can no longer be a RegExp (must be a function). This was deprecated since fs-extra v1.0.0. [#512](https://github.com/jprichardson/node-fs-extra/pull/512) +- `copy()`'s `filter` option can now be a function that returns a Promise. [#518](https://github.com/jprichardson/node-fs-extra/pull/518) +- `copy()` & `copySync()` now use `fs.copyFile()`/`fs.copyFileSync()` in environments that support it (currently Node 8.5.0+). Older Node versions still get the old implementation. [#505](https://github.com/jprichardson/node-fs-extra/pull/505) +- Don't allow copying a directory into itself. [#83](https://github.com/jprichardson/node-fs-extra/issues/83) +- Handle copying between identical files. [#198](https://github.com/jprichardson/node-fs-extra/issues/198) +- Error out when copying an empty folder to a path that already exists. [#464](https://github.com/jprichardson/node-fs-extra/issues/464) +- Don't create `dest`'s parent if the `filter` function aborts the `copy()` operation. [#517](https://github.com/jprichardson/node-fs-extra/pull/517) +- Fix `writeStream` not being closed if there was an error in `copy()`. [#516](https://github.com/jprichardson/node-fs-extra/pull/516) + +4.0.3 / 2017-12-05 +------------------ + +- Fix wrong `chmod` values in `fs.remove()` [#501](https://github.com/jprichardson/node-fs-extra/pull/501) +- Fix `TypeError` on systems that don't have some `fs` operations like `lchown` [#520](https://github.com/jprichardson/node-fs-extra/pull/520) + +4.0.2 / 2017-09-12 +------------------ + +- Added `EOL` option to `writeJson*` & `outputJson*` (via upgrade to jsonfile v4) +- Added promise support to [`fs.copyFile()`](https://nodejs.org/api/fs.html#fs_fs_copyfile_src_dest_flags_callback) in Node 8.5+ +- Added `.js` extension to `main` field in `package.json` for better tooling compatibility. [#485](https://github.com/jprichardson/node-fs-extra/pull/485) + +4.0.1 / 2017-07-31 +------------------ + +### Fixed + +- Previously, `ensureFile()` & `ensureFileSync()` would do nothing if the path was a directory. Now, they error out for consistency with `ensureDir()`. [#465](https://github.com/jprichardson/node-fs-extra/issues/465), [#466](https://github.com/jprichardson/node-fs-extra/pull/466), [#470](https://github.com/jprichardson/node-fs-extra/issues/470) + +4.0.0 / 2017-07-14 +------------------ + +### Changed + +- **BREAKING:** The promisified versions of `fs.read()` & `fs.write()` now return objects. See [the docs](docs/fs-read-write.md) for details. [#436](https://github.com/jprichardson/node-fs-extra/issues/436), [#449](https://github.com/jprichardson/node-fs-extra/pull/449) +- `fs.move()` now errors out when destination is a subdirectory of source. [#458](https://github.com/jprichardson/node-fs-extra/pull/458) +- Applied upstream fixes from `rimraf` to `fs.remove()` & `fs.removeSync()`. [#459](https://github.com/jprichardson/node-fs-extra/pull/459) + +### Fixed + +- Got `fs.outputJSONSync()` working again; it was broken due to refactoring. [#428](https://github.com/jprichardson/node-fs-extra/pull/428) + +Also clarified the docs in a few places. + +3.0.1 / 2017-05-04 +------------------ + +- Fix bug in `move()` & `moveSync()` when source and destination are the same, and source does not exist. [#415](https://github.com/jprichardson/node-fs-extra/pull/415) + +3.0.0 / 2017-04-27 +------------------ + +### Added + +- **BREAKING:** Added Promise support. All asynchronous native fs methods and fs-extra methods now return a promise if the callback is not passed. [#403](https://github.com/jprichardson/node-fs-extra/pull/403) +- `pathExists()`, a replacement for the deprecated `fs.exists`. `pathExists` has a normal error-first callback signature. Also added `pathExistsSync`, an alias to `fs.existsSync`, for completeness. [#406](https://github.com/jprichardson/node-fs-extra/pull/406) + +### Removed + +- **BREAKING:** Removed support for setting the default spaces for `writeJson()`, `writeJsonSync()`, `outputJson()`, & `outputJsonSync()`. This was undocumented. [#402](https://github.com/jprichardson/node-fs-extra/pull/402) + +### Changed + +- Upgraded jsonfile dependency to v3.0.0: + - **BREAKING:** Changed behavior of `throws` option for `readJsonSync()`; now does not throw filesystem errors when `throws` is `false`. +- **BREAKING:** `writeJson()`, `writeJsonSync()`, `outputJson()`, & `outputJsonSync()` now output minified JSON by default for consistency with `JSON.stringify()`; set the `spaces` option to `2` to override this new behavior. [#402](https://github.com/jprichardson/node-fs-extra/pull/402) +- Use `Buffer.allocUnsafe()` instead of `new Buffer()` in environments that support it. [#394](https://github.com/jprichardson/node-fs-extra/pull/394) + +### Fixed + +- `removeSync()` silently failed on Windows in some cases. Now throws an `EBUSY` error. [#408](https://github.com/jprichardson/node-fs-extra/pull/408) + +2.1.2 / 2017-03-16 +------------------ + +### Fixed + +- Weird windows bug that resulted in `ensureDir()`'s callback being called twice in some cases. This bug may have also affected `remove()`. See [#392](https://github.com/jprichardson/node-fs-extra/issues/392), [#393](https://github.com/jprichardson/node-fs-extra/pull/393) + +2.1.1 / 2017-03-15 +------------------ + +### Fixed + +- Reverted [`5597bd`](https://github.com/jprichardson/node-fs-extra/commit/5597bd5b67f7d060f5f5bf26e9635be48330f5d7), this broke compatibility with Node.js versions v4+ but less than `v4.5.0`. +- Remove `Buffer.alloc()` usage in `moveSync()`. + +2.1.0 / 2017-03-15 +------------------ + +Thanks to [Mani Maghsoudlou (@manidlou)](https://github.com/manidlou) & [Jan Peer Stöcklmair (@JPeer264)](https://github.com/JPeer264) for their extraordinary help with this release! + +### Added +- `moveSync()` See [#309], [#381](https://github.com/jprichardson/node-fs-extra/pull/381). ([@manidlou](https://github.com/manidlou)) +- `copy()` and `copySync()`'s `filter` option now gets the destination path passed as the second parameter. [#366](https://github.com/jprichardson/node-fs-extra/pull/366) ([@manidlou](https://github.com/manidlou)) + +### Changed +- Use `Buffer.alloc()` instead of deprecated `new Buffer()` in `copySync()`. [#380](https://github.com/jprichardson/node-fs-extra/pull/380) ([@manidlou](https://github.com/manidlou)) +- Refactored entire codebase to use ES6 features supported by Node.js v4+ [#355](https://github.com/jprichardson/node-fs-extra/issues/355). [(@JPeer264)](https://github.com/JPeer264) +- Refactored docs. ([@manidlou](https://github.com/manidlou)) + +### Fixed + +- `move()` shouldn't error out when source and dest are the same. [#377](https://github.com/jprichardson/node-fs-extra/issues/377), [#378](https://github.com/jprichardson/node-fs-extra/pull/378) ([@jdalton](https://github.com/jdalton)) + +2.0.0 / 2017-01-16 +------------------ + +### Removed +- **BREAKING:** Removed support for Node `v0.12`. The Node foundation stopped officially supporting it +on Jan 1st, 2017. +- **BREAKING:** Remove `walk()` and `walkSync()`. `walkSync()` was only part of `fs-extra` for a little +over two months. Use [klaw](https://github.com/jprichardson/node-klaw) instead of `walk()`, in fact, `walk()` was just +an alias to klaw. For `walkSync()` use [klaw-sync](https://github.com/mawni/node-klaw-sync). See: [#338], [#339] + +### Changed +- **BREAKING:** Renamed `clobber` to `overwrite`. This affects `copy()`, `copySync()`, and `move()`. [#330], [#333] +- Moved docs, to `docs/`. [#340] + +### Fixed +- Apply filters to directories in `copySync()` like in `copy()`. [#324] +- A specific condition when disk is under heavy use, `copy()` can fail. [#326] + + +1.0.0 / 2016-11-01 +------------------ + +After five years of development, we finally have reach the 1.0.0 milestone! Big thanks goes +to [Ryan Zim](https://github.com/RyanZim) for leading the charge on this release! + +### Added +- `walkSync()` + +### Changed +- **BREAKING**: dropped Node v0.10 support. +- disabled `rimaf` globbing, wasn't used. [#280] +- deprecate `copy()/copySync()` option `filter` if it's a `RegExp`. `filter` should now be a function. +- inline `rimraf`. This is temporary and was done because `rimraf` depended upon the beefy `glob` which `fs-extra` does not use. [#300] + +### Fixed +- bug fix proper closing of file handle on `utimesMillis()` [#271] +- proper escaping of files with dollar signs [#291] +- `copySync()` failed if user didn't own file. [#199], [#301] + + +0.30.0 / 2016-04-28 +------------------- +- Brought back Node v0.10 support. I didn't realize there was still demand. Official support will end **2016-10-01**. + +0.29.0 / 2016-04-27 +------------------- +- **BREAKING**: removed support for Node v0.10. If you still want to use Node v0.10, everything should work except for `ensureLink()/ensureSymlink()`. Node v0.12 is still supported but will be dropped in the near future as well. + +0.28.0 / 2016-04-17 +------------------- +- **BREAKING**: removed `createOutputStream()`. Use https://www.npmjs.com/package/create-output-stream. See: [#192][#192] +- `mkdirs()/mkdirsSync()` check for invalid win32 path chars. See: [#209][#209], [#237][#237] +- `mkdirs()/mkdirsSync()` if drive not mounted, error. See: [#93][#93] + +0.27.0 / 2016-04-15 +------------------- +- add `dereference` option to `copySync()`. [#235][#235] + +0.26.7 / 2016-03-16 +------------------- +- fixed `copy()` if source and dest are the same. [#230][#230] + +0.26.6 / 2016-03-15 +------------------- +- fixed if `emptyDir()` does not have a callback: [#229][#229] + +0.26.5 / 2016-01-27 +------------------- +- `copy()` with two arguments (w/o callback) was broken. See: [#215][#215] + +0.26.4 / 2016-01-05 +------------------- +- `copySync()` made `preserveTimestamps` default consistent with `copy()` which is `false`. See: [#208][#208] + +0.26.3 / 2015-12-17 +------------------- +- fixed `copy()` hangup in copying blockDevice / characterDevice / `/dev/null`. See: [#193][#193] + +0.26.2 / 2015-11-02 +------------------- +- fixed `outputJson{Sync}()` spacing adherence to `fs.spaces` + +0.26.1 / 2015-11-02 +------------------- +- fixed `copySync()` when `clogger=true` and the destination is read only. See: [#190][#190] + +0.26.0 / 2015-10-25 +------------------- +- extracted the `walk()` function into its own module [`klaw`](https://github.com/jprichardson/node-klaw). + +0.25.0 / 2015-10-24 +------------------- +- now has a file walker `walk()` + +0.24.0 / 2015-08-28 +------------------- +- removed alias `delete()` and `deleteSync()`. See: [#171][#171] + +0.23.1 / 2015-08-07 +------------------- +- Better handling of errors for `move()` when moving across devices. [#170][#170] +- `ensureSymlink()` and `ensureLink()` should not throw errors if link exists. [#169][#169] + +0.23.0 / 2015-08-06 +------------------- +- added `ensureLink{Sync}()` and `ensureSymlink{Sync}()`. See: [#165][#165] + +0.22.1 / 2015-07-09 +------------------- +- Prevent calling `hasMillisResSync()` on module load. See: [#149][#149]. +Fixes regression that was introduced in `0.21.0`. + +0.22.0 / 2015-07-09 +------------------- +- preserve permissions / ownership in `copy()`. See: [#54][#54] + +0.21.0 / 2015-07-04 +------------------- +- add option to preserve timestamps in `copy()` and `copySync()`. See: [#141][#141] +- updated `graceful-fs@3.x` to `4.x`. This brings in features from `amazing-graceful-fs` (much cleaner code / less hacks) + +0.20.1 / 2015-06-23 +------------------- +- fixed regression caused by latest jsonfile update: See: https://github.com/jprichardson/node-jsonfile/issues/26 + +0.20.0 / 2015-06-19 +------------------- +- removed `jsonfile` aliases with `File` in the name, they weren't documented and probably weren't in use e.g. +this package had both `fs.readJsonFile` and `fs.readJson` that were aliases to each other, now use `fs.readJson`. +- preliminary walker created. Intentionally not documented. If you use it, it will almost certainly change and break your code. +- started moving tests inline +- upgraded to `jsonfile@2.1.0`, can now pass JSON revivers/replacers to `readJson()`, `writeJson()`, `outputJson()` + +0.19.0 / 2015-06-08 +------------------- +- `fs.copy()` had support for Node v0.8, dropped support + +0.18.4 / 2015-05-22 +------------------- +- fixed license field according to this: [#136][#136] and https://github.com/npm/npm/releases/tag/v2.10.0 + +0.18.3 / 2015-05-08 +------------------- +- bugfix: handle `EEXIST` when clobbering on some Linux systems. [#134][#134] + +0.18.2 / 2015-04-17 +------------------- +- bugfix: allow `F_OK` ([#120][#120]) + +0.18.1 / 2015-04-15 +------------------- +- improved windows support for `move()` a bit. https://github.com/jprichardson/node-fs-extra/commit/92838980f25dc2ee4ec46b43ee14d3c4a1d30c1b +- fixed a lot of tests for Windows (appveyor) + +0.18.0 / 2015-03-31 +------------------- +- added `emptyDir()` and `emptyDirSync()` + +0.17.0 / 2015-03-28 +------------------- +- `copySync` added `clobber` option (before always would clobber, now if `clobber` is `false` it throws an error if the destination exists). +**Only works with files at the moment.** +- `createOutputStream()` added. See: [#118][#118] + +0.16.5 / 2015-03-08 +------------------- +- fixed `fs.move` when `clobber` is `true` and destination is a directory, it should clobber. [#114][#114] + +0.16.4 / 2015-03-01 +------------------- +- `fs.mkdirs` fix infinite loop on Windows. See: See https://github.com/substack/node-mkdirp/pull/74 and https://github.com/substack/node-mkdirp/issues/66 + +0.16.3 / 2015-01-28 +------------------- +- reverted https://github.com/jprichardson/node-fs-extra/commit/1ee77c8a805eba5b99382a2591ff99667847c9c9 + + +0.16.2 / 2015-01-28 +------------------- +- fixed `fs.copy` for Node v0.8 (support is temporary and will be removed in the near future) + +0.16.1 / 2015-01-28 +------------------- +- if `setImmediate` is not available, fall back to `process.nextTick` + +0.16.0 / 2015-01-28 +------------------- +- bugfix `fs.move()` into itself. Closes [#104] +- bugfix `fs.move()` moving directory across device. Closes [#108] +- added coveralls support +- bugfix: nasty multiple callback `fs.copy()` bug. Closes [#98] +- misc fs.copy code cleanups + +0.15.0 / 2015-01-21 +------------------- +- dropped `ncp`, imported code in +- because of previous, now supports `io.js` +- `graceful-fs` is now a dependency + +0.14.0 / 2015-01-05 +------------------- +- changed `copy`/`copySync` from `fs.copy(src, dest, [filters], callback)` to `fs.copy(src, dest, [options], callback)` [#100][#100] +- removed mockfs tests for mkdirp (this may be temporary, but was getting in the way of other tests) + +0.13.0 / 2014-12-10 +------------------- +- removed `touch` and `touchSync` methods (they didn't handle permissions like UNIX touch) +- updated `"ncp": "^0.6.0"` to `"ncp": "^1.0.1"` +- imported `mkdirp` => `minimist` and `mkdirp` are no longer dependences, should now appease people who wanted `mkdirp` to be `--use_strict` safe. See [#59]([#59][#59]) + +0.12.0 / 2014-09-22 +------------------- +- copy symlinks in `copySync()` [#85][#85] + +0.11.1 / 2014-09-02 +------------------- +- bugfix `copySync()` preserve file permissions [#80][#80] + +0.11.0 / 2014-08-11 +------------------- +- upgraded `"ncp": "^0.5.1"` to `"ncp": "^0.6.0"` +- upgrade `jsonfile": "^1.2.0"` to `jsonfile": "^2.0.0"` => on write, json files now have `\n` at end. Also adds `options.throws` to `readJsonSync()` +see https://github.com/jprichardson/node-jsonfile#readfilesyncfilename-options for more details. + +0.10.0 / 2014-06-29 +------------------ +* bugfix: upgaded `"jsonfile": "~1.1.0"` to `"jsonfile": "^1.2.0"`, bumped minor because of `jsonfile` dep change +from `~` to `^`. [#67] + +0.9.1 / 2014-05-22 +------------------ +* removed Node.js `0.8.x` support, `0.9.0` was published moments ago and should have been done there + +0.9.0 / 2014-05-22 +------------------ +* upgraded `ncp` from `~0.4.2` to `^0.5.1`, [#58] +* upgraded `rimraf` from `~2.2.6` to `^2.2.8` +* upgraded `mkdirp` from `0.3.x` to `^0.5.0` +* added methods `ensureFile()`, `ensureFileSync()` +* added methods `ensureDir()`, `ensureDirSync()` [#31] +* added `move()` method. From: https://github.com/andrewrk/node-mv + + +0.8.1 / 2013-10-24 +------------------ +* copy failed to return an error to the callback if a file doesn't exist (ulikoehler [#38], [#39]) + +0.8.0 / 2013-10-14 +------------------ +* `filter` implemented on `copy()` and `copySync()`. (Srirangan / [#36]) + +0.7.1 / 2013-10-12 +------------------ +* `copySync()` implemented (Srirangan / [#33]) +* updated to the latest `jsonfile` version `1.1.0` which gives `options` params for the JSON methods. Closes [#32] + +0.7.0 / 2013-10-07 +------------------ +* update readme conventions +* `copy()` now works if destination directory does not exist. Closes [#29] + +0.6.4 / 2013-09-05 +------------------ +* changed `homepage` field in package.json to remove NPM warning + +0.6.3 / 2013-06-28 +------------------ +* changed JSON spacing default from `4` to `2` to follow Node conventions +* updated `jsonfile` dep +* updated `rimraf` dep + +0.6.2 / 2013-06-28 +------------------ +* added .npmignore, [#25] + +0.6.1 / 2013-05-14 +------------------ +* modified for `strict` mode, closes [#24] +* added `outputJson()/outputJsonSync()`, closes [#23] + +0.6.0 / 2013-03-18 +------------------ +* removed node 0.6 support +* added node 0.10 support +* upgraded to latest `ncp` and `rimraf`. +* optional `graceful-fs` support. Closes [#17] + + +0.5.0 / 2013-02-03 +------------------ +* Removed `readTextFile`. +* Renamed `readJSONFile` to `readJSON` and `readJson`, same with write. +* Restructured documentation a bit. Added roadmap. + +0.4.0 / 2013-01-28 +------------------ +* Set default spaces in `jsonfile` from 4 to 2. +* Updated `testutil` deps for tests. +* Renamed `touch()` to `createFile()` +* Added `outputFile()` and `outputFileSync()` +* Changed creation of testing diretories so the /tmp dir is not littered. +* Added `readTextFile()` and `readTextFileSync()`. + +0.3.2 / 2012-11-01 +------------------ +* Added `touch()` and `touchSync()` methods. + +0.3.1 / 2012-10-11 +------------------ +* Fixed some stray globals. + +0.3.0 / 2012-10-09 +------------------ +* Removed all CoffeeScript from tests. +* Renamed `mkdir` to `mkdirs`/`mkdirp`. + +0.2.1 / 2012-09-11 +------------------ +* Updated `rimraf` dep. + +0.2.0 / 2012-09-10 +------------------ +* Rewrote module into JavaScript. (Must still rewrite tests into JavaScript) +* Added all methods of [jsonfile](https://github.com/jprichardson/node-jsonfile) +* Added Travis-CI. + +0.1.3 / 2012-08-13 +------------------ +* Added method `readJSONFile`. + +0.1.2 / 2012-06-15 +------------------ +* Bug fix: `deleteSync()` didn't exist. +* Verified Node v0.8 compatibility. + +0.1.1 / 2012-06-15 +------------------ +* Fixed bug in `remove()`/`delete()` that wouldn't execute the function if a callback wasn't passed. + +0.1.0 / 2012-05-31 +------------------ +* Renamed `copyFile()` to `copy()`. `copy()` can now copy directories (recursively) too. +* Renamed `rmrf()` to `remove()`. +* `remove()` aliased with `delete()`. +* Added `mkdirp` capabilities. Named: `mkdir()`. Hides Node.js native `mkdir()`. +* Instead of exporting the native `fs` module with new functions, I now copy over the native methods to a new object and export that instead. + +0.0.4 / 2012-03-14 +------------------ +* Removed CoffeeScript dependency + +0.0.3 / 2012-01-11 +------------------ +* Added methods rmrf and rmrfSync +* Moved tests from Jasmine to Mocha + + +[#344]: https://github.com/jprichardson/node-fs-extra/issues/344 "Licence Year" +[#343]: https://github.com/jprichardson/node-fs-extra/pull/343 "Add klaw-sync link to readme" +[#342]: https://github.com/jprichardson/node-fs-extra/pull/342 "allow preserveTimestamps when use move" +[#341]: https://github.com/jprichardson/node-fs-extra/issues/341 "mkdirp(path.dirname(dest) in move() logic needs cleaning up [question]" +[#340]: https://github.com/jprichardson/node-fs-extra/pull/340 "Move docs to seperate docs folder [documentation]" +[#339]: https://github.com/jprichardson/node-fs-extra/pull/339 "Remove walk() & walkSync() [feature-walk]" +[#338]: https://github.com/jprichardson/node-fs-extra/issues/338 "Remove walk() and walkSync() [feature-walk]" +[#337]: https://github.com/jprichardson/node-fs-extra/issues/337 "copy doesn't return a yieldable value" +[#336]: https://github.com/jprichardson/node-fs-extra/pull/336 "Docs enhanced walk sync [documentation, feature-walk]" +[#335]: https://github.com/jprichardson/node-fs-extra/pull/335 "Refactor move() tests [feature-move]" +[#334]: https://github.com/jprichardson/node-fs-extra/pull/334 "Cleanup lib/move/index.js [feature-move]" +[#333]: https://github.com/jprichardson/node-fs-extra/pull/333 "Rename clobber to overwrite [feature-copy, feature-move]" +[#332]: https://github.com/jprichardson/node-fs-extra/pull/332 "BREAKING: Drop Node v0.12 & io.js support" +[#331]: https://github.com/jprichardson/node-fs-extra/issues/331 "Add support for chmodr [enhancement, future]" +[#330]: https://github.com/jprichardson/node-fs-extra/pull/330 "BREAKING: Do not error when copy destination exists & clobber: false [feature-copy]" +[#329]: https://github.com/jprichardson/node-fs-extra/issues/329 "Does .walk() scale to large directories? [question]" +[#328]: https://github.com/jprichardson/node-fs-extra/issues/328 "Copying files corrupts [feature-copy, needs-confirmed]" +[#327]: https://github.com/jprichardson/node-fs-extra/pull/327 "Use writeStream 'finish' event instead of 'close' [bug, feature-copy]" +[#326]: https://github.com/jprichardson/node-fs-extra/issues/326 "fs.copy fails with chmod error when disk under heavy use [bug, feature-copy]" +[#325]: https://github.com/jprichardson/node-fs-extra/issues/325 "ensureDir is difficult to promisify [enhancement]" +[#324]: https://github.com/jprichardson/node-fs-extra/pull/324 "copySync() should apply filter to directories like copy() [bug, feature-copy]" +[#323]: https://github.com/jprichardson/node-fs-extra/issues/323 "Support for `dest` being a directory when using `copy*()`?" +[#322]: https://github.com/jprichardson/node-fs-extra/pull/322 "Add fs-promise as fs-extra-promise alternative" +[#321]: https://github.com/jprichardson/node-fs-extra/issues/321 "fs.copy() with clobber set to false return EEXIST error [feature-copy]" +[#320]: https://github.com/jprichardson/node-fs-extra/issues/320 "fs.copySync: Error: EPERM: operation not permitted, unlink " +[#319]: https://github.com/jprichardson/node-fs-extra/issues/319 "Create directory if not exists" +[#318]: https://github.com/jprichardson/node-fs-extra/issues/318 "Support glob patterns [enhancement, future]" +[#317]: https://github.com/jprichardson/node-fs-extra/pull/317 "Adding copy sync test for src file without write perms" +[#316]: https://github.com/jprichardson/node-fs-extra/pull/316 "Remove move()'s broken limit option [feature-move]" +[#315]: https://github.com/jprichardson/node-fs-extra/pull/315 "Fix move clobber tests to work around graceful-fs bug." +[#314]: https://github.com/jprichardson/node-fs-extra/issues/314 "move() limit option [documentation, enhancement, feature-move]" +[#313]: https://github.com/jprichardson/node-fs-extra/pull/313 "Test that remove() ignores glob characters." +[#312]: https://github.com/jprichardson/node-fs-extra/pull/312 "Enhance walkSync() to return items with path and stats [feature-walk]" +[#311]: https://github.com/jprichardson/node-fs-extra/issues/311 "move() not work when dest name not provided [feature-move]" +[#310]: https://github.com/jprichardson/node-fs-extra/issues/310 "Edit walkSync to return items like what walk emits [documentation, enhancement, feature-walk]" +[#309]: https://github.com/jprichardson/node-fs-extra/issues/309 "moveSync support [enhancement, feature-move]" +[#308]: https://github.com/jprichardson/node-fs-extra/pull/308 "Fix incorrect anchor link" +[#307]: https://github.com/jprichardson/node-fs-extra/pull/307 "Fix coverage" +[#306]: https://github.com/jprichardson/node-fs-extra/pull/306 "Update devDeps, fix lint error" +[#305]: https://github.com/jprichardson/node-fs-extra/pull/305 "Re-add Coveralls" +[#304]: https://github.com/jprichardson/node-fs-extra/pull/304 "Remove path-is-absolute [enhancement]" +[#303]: https://github.com/jprichardson/node-fs-extra/pull/303 "Document copySync filter inconsistency [documentation, feature-copy]" +[#302]: https://github.com/jprichardson/node-fs-extra/pull/302 "fix(console): depreciated -> deprecated" +[#301]: https://github.com/jprichardson/node-fs-extra/pull/301 "Remove chmod call from copySync [feature-copy]" +[#300]: https://github.com/jprichardson/node-fs-extra/pull/300 "Inline Rimraf [enhancement, feature-move, feature-remove]" +[#299]: https://github.com/jprichardson/node-fs-extra/pull/299 "Warn when filter is a RegExp [feature-copy]" +[#298]: https://github.com/jprichardson/node-fs-extra/issues/298 "API Docs [documentation]" +[#297]: https://github.com/jprichardson/node-fs-extra/pull/297 "Warn about using preserveTimestamps on 32-bit node" +[#296]: https://github.com/jprichardson/node-fs-extra/pull/296 "Improve EEXIST error message for copySync [enhancement]" +[#295]: https://github.com/jprichardson/node-fs-extra/pull/295 "Depreciate using regular expressions for copy's filter option [documentation]" +[#294]: https://github.com/jprichardson/node-fs-extra/pull/294 "BREAKING: Refactor lib/copy/ncp.js [feature-copy]" +[#293]: https://github.com/jprichardson/node-fs-extra/pull/293 "Update CI configs" +[#292]: https://github.com/jprichardson/node-fs-extra/issues/292 "Rewrite lib/copy/ncp.js [enhancement, feature-copy]" +[#291]: https://github.com/jprichardson/node-fs-extra/pull/291 "Escape '$' in replacement string for async file copying" +[#290]: https://github.com/jprichardson/node-fs-extra/issues/290 "Exclude files pattern while copying using copy.config.js [question]" +[#289]: https://github.com/jprichardson/node-fs-extra/pull/289 "(Closes #271) lib/util/utimes: properly close file descriptors in the event of an error" +[#288]: https://github.com/jprichardson/node-fs-extra/pull/288 "(Closes #271) lib/util/utimes: properly close file descriptors in the event of an error" +[#287]: https://github.com/jprichardson/node-fs-extra/issues/287 "emptyDir() callback arguments are inconsistent [enhancement, feature-remove]" +[#286]: https://github.com/jprichardson/node-fs-extra/pull/286 "Added walkSync function" +[#285]: https://github.com/jprichardson/node-fs-extra/issues/285 "CITGM test failing on s390" +[#284]: https://github.com/jprichardson/node-fs-extra/issues/284 "outputFile method is missing a check to determine if existing item is a folder or not" +[#283]: https://github.com/jprichardson/node-fs-extra/pull/283 "Apply filter also on directories and symlinks for copySync()" +[#282]: https://github.com/jprichardson/node-fs-extra/pull/282 "Apply filter also on directories and symlinks for copySync()" +[#281]: https://github.com/jprichardson/node-fs-extra/issues/281 "remove function executes 'successfully' but doesn't do anything?" +[#280]: https://github.com/jprichardson/node-fs-extra/pull/280 "Disable rimraf globbing" +[#279]: https://github.com/jprichardson/node-fs-extra/issues/279 "Some code is vendored instead of included [awaiting-reply]" +[#278]: https://github.com/jprichardson/node-fs-extra/issues/278 "copy() does not preserve file/directory ownership" +[#277]: https://github.com/jprichardson/node-fs-extra/pull/277 "Mention defaults for clobber and dereference options" +[#276]: https://github.com/jprichardson/node-fs-extra/issues/276 "Cannot connect to Shared Folder [awaiting-reply]" +[#275]: https://github.com/jprichardson/node-fs-extra/issues/275 "EMFILE, too many open files on Mac OS with JSON API" +[#274]: https://github.com/jprichardson/node-fs-extra/issues/274 "Use with memory-fs? [enhancement, future]" +[#273]: https://github.com/jprichardson/node-fs-extra/pull/273 "tests: rename `remote.test.js` to `remove.test.js`" +[#272]: https://github.com/jprichardson/node-fs-extra/issues/272 "Copy clobber flag never err even when true [bug, feature-copy]" +[#271]: https://github.com/jprichardson/node-fs-extra/issues/271 "Unclosed file handle on futimes error" +[#270]: https://github.com/jprichardson/node-fs-extra/issues/270 "copy not working as desired on Windows [feature-copy, platform-windows]" +[#269]: https://github.com/jprichardson/node-fs-extra/issues/269 "Copying with preserveTimeStamps: true is inaccurate using 32bit node [feature-copy]" +[#268]: https://github.com/jprichardson/node-fs-extra/pull/268 "port fix for mkdirp issue #111" +[#267]: https://github.com/jprichardson/node-fs-extra/issues/267 "WARN deprecated wrench@1.5.9: wrench.js is deprecated!" +[#266]: https://github.com/jprichardson/node-fs-extra/issues/266 "fs-extra" +[#265]: https://github.com/jprichardson/node-fs-extra/issues/265 "Link the `fs.stat fs.exists` etc. methods for replace the `fs` module forever?" +[#264]: https://github.com/jprichardson/node-fs-extra/issues/264 "Renaming a file using move fails when a file inside is open (at least on windows) [wont-fix]" +[#263]: https://github.com/jprichardson/node-fs-extra/issues/263 "ENOSYS: function not implemented, link [needs-confirmed]" +[#262]: https://github.com/jprichardson/node-fs-extra/issues/262 "Add .exists() and .existsSync()" +[#261]: https://github.com/jprichardson/node-fs-extra/issues/261 "Cannot read property 'prototype' of undefined" +[#260]: https://github.com/jprichardson/node-fs-extra/pull/260 "use more specific path for method require" +[#259]: https://github.com/jprichardson/node-fs-extra/issues/259 "Feature Request: isEmpty" +[#258]: https://github.com/jprichardson/node-fs-extra/issues/258 "copy files does not preserve file timestamp" +[#257]: https://github.com/jprichardson/node-fs-extra/issues/257 "Copying a file on windows fails" +[#256]: https://github.com/jprichardson/node-fs-extra/pull/256 "Updated Readme " +[#255]: https://github.com/jprichardson/node-fs-extra/issues/255 "Update rimraf required version" +[#254]: https://github.com/jprichardson/node-fs-extra/issues/254 "request for readTree, readTreeSync, walkSync method" +[#253]: https://github.com/jprichardson/node-fs-extra/issues/253 "outputFile does not touch mtime when file exists" +[#252]: https://github.com/jprichardson/node-fs-extra/pull/252 "Fixing problem when copying file with no write permission" +[#251]: https://github.com/jprichardson/node-fs-extra/issues/251 "Just wanted to say thank you" +[#250]: https://github.com/jprichardson/node-fs-extra/issues/250 "`fs.remove()` not removing files (works with `rm -rf`)" +[#249]: https://github.com/jprichardson/node-fs-extra/issues/249 "Just a Question ... Remove Servers" +[#248]: https://github.com/jprichardson/node-fs-extra/issues/248 "Allow option to not preserve permissions for copy" +[#247]: https://github.com/jprichardson/node-fs-extra/issues/247 "Add TypeScript typing directly in the fs-extra package" +[#246]: https://github.com/jprichardson/node-fs-extra/issues/246 "fse.remove() && fse.removeSync() don't throw error on ENOENT file" +[#245]: https://github.com/jprichardson/node-fs-extra/issues/245 "filter for empty dir [enhancement]" +[#244]: https://github.com/jprichardson/node-fs-extra/issues/244 "copySync doesn't apply the filter to directories" +[#243]: https://github.com/jprichardson/node-fs-extra/issues/243 "Can I request fs.walk() to be synchronous?" +[#242]: https://github.com/jprichardson/node-fs-extra/issues/242 "Accidentally truncates file names ending with $$ [bug, feature-copy]" +[#241]: https://github.com/jprichardson/node-fs-extra/pull/241 "Remove link to createOutputStream" +[#240]: https://github.com/jprichardson/node-fs-extra/issues/240 "walkSync request" +[#239]: https://github.com/jprichardson/node-fs-extra/issues/239 "Depreciate regular expressions for copy's filter [documentation, feature-copy]" +[#238]: https://github.com/jprichardson/node-fs-extra/issues/238 "Can't write to files while in a worker thread." +[#237]: https://github.com/jprichardson/node-fs-extra/issues/237 ".ensureDir(..) fails silently when passed an invalid path..." +[#236]: https://github.com/jprichardson/node-fs-extra/issues/236 "[Removed] Filed under wrong repo" +[#235]: https://github.com/jprichardson/node-fs-extra/pull/235 "Adds symlink dereference option to `fse.copySync` (#191)" +[#234]: https://github.com/jprichardson/node-fs-extra/issues/234 "ensureDirSync fails silent when EACCES: permission denied on travis-ci" +[#233]: https://github.com/jprichardson/node-fs-extra/issues/233 "please make sure the first argument in callback is error object [feature-copy]" +[#232]: https://github.com/jprichardson/node-fs-extra/issues/232 "Copy a folder content to its child folder. " +[#231]: https://github.com/jprichardson/node-fs-extra/issues/231 "Adding read/write/output functions for YAML" +[#230]: https://github.com/jprichardson/node-fs-extra/pull/230 "throw error if src and dest are the same to avoid zeroing out + test" +[#229]: https://github.com/jprichardson/node-fs-extra/pull/229 "fix 'TypeError: callback is not a function' in emptyDir" +[#228]: https://github.com/jprichardson/node-fs-extra/pull/228 "Throw error when target is empty so file is not accidentally zeroed out" +[#227]: https://github.com/jprichardson/node-fs-extra/issues/227 "Uncatchable errors when there are invalid arguments [feature-move]" +[#226]: https://github.com/jprichardson/node-fs-extra/issues/226 "Moving to the current directory" +[#225]: https://github.com/jprichardson/node-fs-extra/issues/225 "EBUSY: resource busy or locked, unlink" +[#224]: https://github.com/jprichardson/node-fs-extra/issues/224 "fse.copy ENOENT error" +[#223]: https://github.com/jprichardson/node-fs-extra/issues/223 "Suspicious behavior of fs.existsSync" +[#222]: https://github.com/jprichardson/node-fs-extra/pull/222 "A clearer description of emtpyDir function" +[#221]: https://github.com/jprichardson/node-fs-extra/pull/221 "Update README.md" +[#220]: https://github.com/jprichardson/node-fs-extra/pull/220 "Non-breaking feature: add option 'passStats' to copy methods." +[#219]: https://github.com/jprichardson/node-fs-extra/pull/219 "Add closing parenthesis in copySync example" +[#218]: https://github.com/jprichardson/node-fs-extra/pull/218 "fix #187 #70 options.filter bug" +[#217]: https://github.com/jprichardson/node-fs-extra/pull/217 "fix #187 #70 options.filter bug" +[#216]: https://github.com/jprichardson/node-fs-extra/pull/216 "fix #187 #70 options.filter bug" +[#215]: https://github.com/jprichardson/node-fs-extra/pull/215 "fse.copy throws error when only src and dest provided [bug, documentation, feature-copy]" +[#214]: https://github.com/jprichardson/node-fs-extra/pull/214 "Fixing copySync anchor tag" +[#213]: https://github.com/jprichardson/node-fs-extra/issues/213 "Merge extfs with this repo" +[#212]: https://github.com/jprichardson/node-fs-extra/pull/212 "Update year to 2016 in README.md and LICENSE" +[#211]: https://github.com/jprichardson/node-fs-extra/issues/211 "Not copying all files" +[#210]: https://github.com/jprichardson/node-fs-extra/issues/210 "copy/copySync behave differently when copying a symbolic file [bug, documentation, feature-copy]" +[#209]: https://github.com/jprichardson/node-fs-extra/issues/209 "In Windows invalid directory name causes infinite loop in ensureDir(). [bug]" +[#208]: https://github.com/jprichardson/node-fs-extra/pull/208 "fix options.preserveTimestamps to false in copy-sync by default [feature-copy]" +[#207]: https://github.com/jprichardson/node-fs-extra/issues/207 "Add `compare` suite of functions" +[#206]: https://github.com/jprichardson/node-fs-extra/issues/206 "outputFileSync" +[#205]: https://github.com/jprichardson/node-fs-extra/issues/205 "fix documents about copy/copySync [documentation, feature-copy]" +[#204]: https://github.com/jprichardson/node-fs-extra/pull/204 "allow copy of block and character device files" +[#203]: https://github.com/jprichardson/node-fs-extra/issues/203 "copy method's argument options couldn't be undefined [bug, feature-copy]" +[#202]: https://github.com/jprichardson/node-fs-extra/issues/202 "why there is not a walkSync method?" +[#201]: https://github.com/jprichardson/node-fs-extra/issues/201 "clobber for directories [feature-copy, future]" +[#200]: https://github.com/jprichardson/node-fs-extra/issues/200 "'copySync' doesn't work in sync" +[#199]: https://github.com/jprichardson/node-fs-extra/issues/199 "fs.copySync fails if user does not own file [bug, feature-copy]" +[#198]: https://github.com/jprichardson/node-fs-extra/issues/198 "handle copying between identical files [feature-copy]" +[#197]: https://github.com/jprichardson/node-fs-extra/issues/197 "Missing documentation for `outputFile` `options` 3rd parameter [documentation]" +[#196]: https://github.com/jprichardson/node-fs-extra/issues/196 "copy filter: async function and/or function called with `fs.stat` result [future]" +[#195]: https://github.com/jprichardson/node-fs-extra/issues/195 "How to override with outputFile?" +[#194]: https://github.com/jprichardson/node-fs-extra/pull/194 "allow ensureFile(Sync) to provide data to be written to created file" +[#193]: https://github.com/jprichardson/node-fs-extra/issues/193 "`fs.copy` fails silently if source file is /dev/null [bug, feature-copy]" +[#192]: https://github.com/jprichardson/node-fs-extra/issues/192 "Remove fs.createOutputStream()" +[#191]: https://github.com/jprichardson/node-fs-extra/issues/191 "How to copy symlinks to target as normal folders [feature-copy]" +[#190]: https://github.com/jprichardson/node-fs-extra/pull/190 "copySync to overwrite destination file if readonly and clobber true" +[#189]: https://github.com/jprichardson/node-fs-extra/pull/189 "move.test fix to support CRLF on Windows" +[#188]: https://github.com/jprichardson/node-fs-extra/issues/188 "move.test failing on windows platform" +[#187]: https://github.com/jprichardson/node-fs-extra/issues/187 "Not filter each file, stops on first false [feature-copy]" +[#186]: https://github.com/jprichardson/node-fs-extra/issues/186 "Do you need a .size() function in this module? [future]" +[#185]: https://github.com/jprichardson/node-fs-extra/issues/185 "Doesn't work on NodeJS v4.x" +[#184]: https://github.com/jprichardson/node-fs-extra/issues/184 "CLI equivalent for fs-extra" +[#183]: https://github.com/jprichardson/node-fs-extra/issues/183 "with clobber true, copy and copySync behave differently if destination file is read only [bug, feature-copy]" +[#182]: https://github.com/jprichardson/node-fs-extra/issues/182 "ensureDir(dir, callback) second callback parameter not specified" +[#181]: https://github.com/jprichardson/node-fs-extra/issues/181 "Add ability to remove file securely [enhancement, wont-fix]" +[#180]: https://github.com/jprichardson/node-fs-extra/issues/180 "Filter option doesn't work the same way in copy and copySync [bug, feature-copy]" +[#179]: https://github.com/jprichardson/node-fs-extra/issues/179 "Include opendir" +[#178]: https://github.com/jprichardson/node-fs-extra/issues/178 "ENOTEMPTY is thrown on removeSync " +[#177]: https://github.com/jprichardson/node-fs-extra/issues/177 "fix `remove()` wildcards (introduced by rimraf) [feature-remove]" +[#176]: https://github.com/jprichardson/node-fs-extra/issues/176 "createOutputStream doesn't emit 'end' event" +[#175]: https://github.com/jprichardson/node-fs-extra/issues/175 "[Feature Request].moveSync support [feature-move, future]" +[#174]: https://github.com/jprichardson/node-fs-extra/pull/174 "Fix copy formatting and document options.filter" +[#173]: https://github.com/jprichardson/node-fs-extra/issues/173 "Feature Request: writeJson should mkdirs" +[#172]: https://github.com/jprichardson/node-fs-extra/issues/172 "rename `clobber` flags to `overwrite`" +[#171]: https://github.com/jprichardson/node-fs-extra/issues/171 "remove unnecessary aliases" +[#170]: https://github.com/jprichardson/node-fs-extra/pull/170 "More robust handling of errors moving across virtual drives" +[#169]: https://github.com/jprichardson/node-fs-extra/pull/169 "suppress ensureLink & ensureSymlink dest exists error" +[#168]: https://github.com/jprichardson/node-fs-extra/pull/168 "suppress ensurelink dest exists error" +[#167]: https://github.com/jprichardson/node-fs-extra/pull/167 "Adds basic (string, buffer) support for ensureFile content [future]" +[#166]: https://github.com/jprichardson/node-fs-extra/pull/166 "Adds basic (string, buffer) support for ensureFile content" +[#165]: https://github.com/jprichardson/node-fs-extra/pull/165 "ensure for link & symlink" +[#164]: https://github.com/jprichardson/node-fs-extra/issues/164 "Feature Request: ensureFile to take optional argument for file content" +[#163]: https://github.com/jprichardson/node-fs-extra/issues/163 "ouputJson not formatted out of the box [bug]" +[#162]: https://github.com/jprichardson/node-fs-extra/pull/162 "ensure symlink & link" +[#161]: https://github.com/jprichardson/node-fs-extra/pull/161 "ensure symlink & link" +[#160]: https://github.com/jprichardson/node-fs-extra/pull/160 "ensure symlink & link" +[#159]: https://github.com/jprichardson/node-fs-extra/pull/159 "ensure symlink & link" +[#158]: https://github.com/jprichardson/node-fs-extra/issues/158 "Feature Request: ensureLink and ensureSymlink methods" +[#157]: https://github.com/jprichardson/node-fs-extra/issues/157 "writeJson isn't formatted" +[#156]: https://github.com/jprichardson/node-fs-extra/issues/156 "Promise.promisifyAll doesn't work for some methods" +[#155]: https://github.com/jprichardson/node-fs-extra/issues/155 "Readme" +[#154]: https://github.com/jprichardson/node-fs-extra/issues/154 "/tmp/millis-test-sync" +[#153]: https://github.com/jprichardson/node-fs-extra/pull/153 "Make preserveTimes also work on read-only files. Closes #152" +[#152]: https://github.com/jprichardson/node-fs-extra/issues/152 "fs.copy fails for read-only files with preserveTimestamp=true [feature-copy]" +[#151]: https://github.com/jprichardson/node-fs-extra/issues/151 "TOC does not work correctly on npm [documentation]" +[#150]: https://github.com/jprichardson/node-fs-extra/issues/150 "Remove test file fixtures, create with code." +[#149]: https://github.com/jprichardson/node-fs-extra/issues/149 "/tmp/millis-test-sync" +[#148]: https://github.com/jprichardson/node-fs-extra/issues/148 "split out `Sync` methods in documentation" +[#147]: https://github.com/jprichardson/node-fs-extra/issues/147 "Adding rmdirIfEmpty" +[#146]: https://github.com/jprichardson/node-fs-extra/pull/146 "ensure test.js works" +[#145]: https://github.com/jprichardson/node-fs-extra/issues/145 "Add `fs.exists` and `fs.existsSync` if it doesn't exist." +[#144]: https://github.com/jprichardson/node-fs-extra/issues/144 "tests failing" +[#143]: https://github.com/jprichardson/node-fs-extra/issues/143 "update graceful-fs" +[#142]: https://github.com/jprichardson/node-fs-extra/issues/142 "PrependFile Feature" +[#141]: https://github.com/jprichardson/node-fs-extra/pull/141 "Add option to preserve timestamps" +[#140]: https://github.com/jprichardson/node-fs-extra/issues/140 "Json file reading fails with 'utf8'" +[#139]: https://github.com/jprichardson/node-fs-extra/pull/139 "Preserve file timestamp on copy. Closes #138" +[#138]: https://github.com/jprichardson/node-fs-extra/issues/138 "Preserve timestamps on copying files" +[#137]: https://github.com/jprichardson/node-fs-extra/issues/137 "outputFile/outputJson: Unexpected end of input" +[#136]: https://github.com/jprichardson/node-fs-extra/pull/136 "Update license attribute" +[#135]: https://github.com/jprichardson/node-fs-extra/issues/135 "emptyDir throws Error if no callback is provided" +[#134]: https://github.com/jprichardson/node-fs-extra/pull/134 "Handle EEXIST error when clobbering dir" +[#133]: https://github.com/jprichardson/node-fs-extra/pull/133 "Travis runs with `sudo: false`" +[#132]: https://github.com/jprichardson/node-fs-extra/pull/132 "isDirectory method" +[#131]: https://github.com/jprichardson/node-fs-extra/issues/131 "copySync is not working iojs 1.8.4 on linux [feature-copy]" +[#130]: https://github.com/jprichardson/node-fs-extra/pull/130 "Please review additional features." +[#129]: https://github.com/jprichardson/node-fs-extra/pull/129 "can you review this feature?" +[#128]: https://github.com/jprichardson/node-fs-extra/issues/128 "fsExtra.move(filepath, newPath) broken;" +[#127]: https://github.com/jprichardson/node-fs-extra/issues/127 "consider using fs.access to remove deprecated warnings for fs.exists" +[#126]: https://github.com/jprichardson/node-fs-extra/issues/126 " TypeError: Object # has no method 'access'" +[#125]: https://github.com/jprichardson/node-fs-extra/issues/125 "Question: What do the *Sync function do different from non-sync" +[#124]: https://github.com/jprichardson/node-fs-extra/issues/124 "move with clobber option 'ENOTEMPTY'" +[#123]: https://github.com/jprichardson/node-fs-extra/issues/123 "Only copy the content of a directory" +[#122]: https://github.com/jprichardson/node-fs-extra/pull/122 "Update section links in README to match current section ids." +[#121]: https://github.com/jprichardson/node-fs-extra/issues/121 "emptyDir is undefined" +[#120]: https://github.com/jprichardson/node-fs-extra/issues/120 "usage bug caused by shallow cloning methods of 'graceful-fs'" +[#119]: https://github.com/jprichardson/node-fs-extra/issues/119 "mkdirs and ensureDir never invoke callback and consume CPU indefinitely if provided a path with invalid characters on Windows" +[#118]: https://github.com/jprichardson/node-fs-extra/pull/118 "createOutputStream" +[#117]: https://github.com/jprichardson/node-fs-extra/pull/117 "Fixed issue with slash separated paths on windows" +[#116]: https://github.com/jprichardson/node-fs-extra/issues/116 "copySync can only copy directories not files [documentation, feature-copy]" +[#115]: https://github.com/jprichardson/node-fs-extra/issues/115 ".Copy & .CopySync [feature-copy]" +[#114]: https://github.com/jprichardson/node-fs-extra/issues/114 "Fails to move (rename) directory to non-empty directory even with clobber: true" +[#113]: https://github.com/jprichardson/node-fs-extra/issues/113 "fs.copy seems to callback early if the destination file already exists" +[#112]: https://github.com/jprichardson/node-fs-extra/pull/112 "Copying a file into an existing directory" +[#111]: https://github.com/jprichardson/node-fs-extra/pull/111 "Moving a file into an existing directory " +[#110]: https://github.com/jprichardson/node-fs-extra/pull/110 "Moving a file into an existing directory" +[#109]: https://github.com/jprichardson/node-fs-extra/issues/109 "fs.move across windows drives fails" +[#108]: https://github.com/jprichardson/node-fs-extra/issues/108 "fse.move directories across multiple devices doesn't work" +[#107]: https://github.com/jprichardson/node-fs-extra/pull/107 "Check if dest path is an existing dir and copy or move source in it" +[#106]: https://github.com/jprichardson/node-fs-extra/issues/106 "fse.copySync crashes while copying across devices D: [feature-copy]" +[#105]: https://github.com/jprichardson/node-fs-extra/issues/105 "fs.copy hangs on iojs" +[#104]: https://github.com/jprichardson/node-fs-extra/issues/104 "fse.move deletes folders [bug]" +[#103]: https://github.com/jprichardson/node-fs-extra/issues/103 "Error: EMFILE with copy" +[#102]: https://github.com/jprichardson/node-fs-extra/issues/102 "touch / touchSync was removed ?" +[#101]: https://github.com/jprichardson/node-fs-extra/issues/101 "fs-extra promisified" +[#100]: https://github.com/jprichardson/node-fs-extra/pull/100 "copy: options object or filter to pass to ncp" +[#99]: https://github.com/jprichardson/node-fs-extra/issues/99 "ensureDir() modes [future]" +[#98]: https://github.com/jprichardson/node-fs-extra/issues/98 "fs.copy() incorrect async behavior [bug]" +[#97]: https://github.com/jprichardson/node-fs-extra/pull/97 "use path.join; fix copySync bug" +[#96]: https://github.com/jprichardson/node-fs-extra/issues/96 "destFolderExists in copySync is always undefined." +[#95]: https://github.com/jprichardson/node-fs-extra/pull/95 "Using graceful-ncp instead of ncp" +[#94]: https://github.com/jprichardson/node-fs-extra/issues/94 "Error: EEXIST, file already exists '../mkdirp/bin/cmd.js' on fs.copySync() [enhancement, feature-copy]" +[#93]: https://github.com/jprichardson/node-fs-extra/issues/93 "Confusing error if drive not mounted [enhancement]" +[#92]: https://github.com/jprichardson/node-fs-extra/issues/92 "Problems with Bluebird" +[#91]: https://github.com/jprichardson/node-fs-extra/issues/91 "fs.copySync('/test', '/haha') is different with 'cp -r /test /haha' [enhancement]" +[#90]: https://github.com/jprichardson/node-fs-extra/issues/90 "Folder creation and file copy is Happening in 64 bit machine but not in 32 bit machine" +[#89]: https://github.com/jprichardson/node-fs-extra/issues/89 "Error: EEXIST using fs-extra's fs.copy to copy a directory on Windows" +[#88]: https://github.com/jprichardson/node-fs-extra/issues/88 "Stacking those libraries" +[#87]: https://github.com/jprichardson/node-fs-extra/issues/87 "createWriteStream + outputFile = ?" +[#86]: https://github.com/jprichardson/node-fs-extra/issues/86 "no moveSync?" +[#85]: https://github.com/jprichardson/node-fs-extra/pull/85 "Copy symlinks in copySync" +[#84]: https://github.com/jprichardson/node-fs-extra/issues/84 "Push latest version to npm ?" +[#83]: https://github.com/jprichardson/node-fs-extra/issues/83 "Prevent copying a directory into itself [feature-copy]" +[#82]: https://github.com/jprichardson/node-fs-extra/pull/82 "README updates for move" +[#81]: https://github.com/jprichardson/node-fs-extra/issues/81 "fd leak after fs.move" +[#80]: https://github.com/jprichardson/node-fs-extra/pull/80 "Preserve file mode in copySync" +[#79]: https://github.com/jprichardson/node-fs-extra/issues/79 "fs.copy only .html file empty" +[#78]: https://github.com/jprichardson/node-fs-extra/pull/78 "copySync was not applying filters to directories" +[#77]: https://github.com/jprichardson/node-fs-extra/issues/77 "Create README reference to bluebird" +[#76]: https://github.com/jprichardson/node-fs-extra/issues/76 "Create README reference to typescript" +[#75]: https://github.com/jprichardson/node-fs-extra/issues/75 "add glob as a dep? [question]" +[#74]: https://github.com/jprichardson/node-fs-extra/pull/74 "including new emptydir module" +[#73]: https://github.com/jprichardson/node-fs-extra/pull/73 "add dependency status in readme" +[#72]: https://github.com/jprichardson/node-fs-extra/pull/72 "Use svg instead of png to get better image quality" +[#71]: https://github.com/jprichardson/node-fs-extra/issues/71 "fse.copy not working on Windows 7 x64 OS, but, copySync does work" +[#70]: https://github.com/jprichardson/node-fs-extra/issues/70 "Not filter each file, stops on first false [bug]" +[#69]: https://github.com/jprichardson/node-fs-extra/issues/69 "How to check if folder exist and read the folder name" +[#68]: https://github.com/jprichardson/node-fs-extra/issues/68 "consider flag to readJsonSync (throw false) [enhancement]" +[#67]: https://github.com/jprichardson/node-fs-extra/issues/67 "docs for readJson incorrectly states that is accepts options" +[#66]: https://github.com/jprichardson/node-fs-extra/issues/66 "ENAMETOOLONG" +[#65]: https://github.com/jprichardson/node-fs-extra/issues/65 "exclude filter in fs.copy" +[#64]: https://github.com/jprichardson/node-fs-extra/issues/64 "Announce: mfs - monitor your fs-extra calls" +[#63]: https://github.com/jprichardson/node-fs-extra/issues/63 "Walk" +[#62]: https://github.com/jprichardson/node-fs-extra/issues/62 "npm install fs-extra doesn't work" +[#61]: https://github.com/jprichardson/node-fs-extra/issues/61 "No longer supports node 0.8 due to use of `^` in package.json dependencies" +[#60]: https://github.com/jprichardson/node-fs-extra/issues/60 "chmod & chown for mkdirs" +[#59]: https://github.com/jprichardson/node-fs-extra/issues/59 "Consider including mkdirp and making fs-extra '--use_strict' safe [question]" +[#58]: https://github.com/jprichardson/node-fs-extra/issues/58 "Stack trace not included in fs.copy error" +[#57]: https://github.com/jprichardson/node-fs-extra/issues/57 "Possible to include wildcards in delete?" +[#56]: https://github.com/jprichardson/node-fs-extra/issues/56 "Crash when have no access to write to destination file in copy " +[#55]: https://github.com/jprichardson/node-fs-extra/issues/55 "Is it possible to have any console output similar to Grunt copy module?" +[#54]: https://github.com/jprichardson/node-fs-extra/issues/54 "`copy` does not preserve file ownership and permissons" +[#53]: https://github.com/jprichardson/node-fs-extra/issues/53 "outputFile() - ability to write data in appending mode" +[#52]: https://github.com/jprichardson/node-fs-extra/pull/52 "This fixes (what I think) is a bug in copySync" +[#51]: https://github.com/jprichardson/node-fs-extra/pull/51 "Add a Bitdeli Badge to README" +[#50]: https://github.com/jprichardson/node-fs-extra/issues/50 "Replace mechanism in createFile" +[#49]: https://github.com/jprichardson/node-fs-extra/pull/49 "update rimraf to v2.2.6" +[#48]: https://github.com/jprichardson/node-fs-extra/issues/48 "fs.copy issue [bug]" +[#47]: https://github.com/jprichardson/node-fs-extra/issues/47 "Bug in copy - callback called on readStream 'close' - Fixed in ncp 0.5.0" +[#46]: https://github.com/jprichardson/node-fs-extra/pull/46 "update copyright year" +[#45]: https://github.com/jprichardson/node-fs-extra/pull/45 "Added note about fse.outputFile() being the one that overwrites" +[#44]: https://github.com/jprichardson/node-fs-extra/pull/44 "Proposal: Stream support" +[#43]: https://github.com/jprichardson/node-fs-extra/issues/43 "Better error reporting " +[#42]: https://github.com/jprichardson/node-fs-extra/issues/42 "Performance issue?" +[#41]: https://github.com/jprichardson/node-fs-extra/pull/41 "There does seem to be a synchronous version now" +[#40]: https://github.com/jprichardson/node-fs-extra/issues/40 "fs.copy throw unexplained error ENOENT, utime " +[#39]: https://github.com/jprichardson/node-fs-extra/pull/39 "Added regression test for copy() return callback on error" +[#38]: https://github.com/jprichardson/node-fs-extra/pull/38 "Return err in copy() fstat cb, because stat could be undefined or null" +[#37]: https://github.com/jprichardson/node-fs-extra/issues/37 "Maybe include a line reader? [enhancement, question]" +[#36]: https://github.com/jprichardson/node-fs-extra/pull/36 "`filter` parameter `fs.copy` and `fs.copySync`" +[#35]: https://github.com/jprichardson/node-fs-extra/pull/35 "`filter` parameter `fs.copy` and `fs.copySync` " +[#34]: https://github.com/jprichardson/node-fs-extra/issues/34 "update docs to include options for JSON methods [enhancement]" +[#33]: https://github.com/jprichardson/node-fs-extra/pull/33 "fs_extra.copySync" +[#32]: https://github.com/jprichardson/node-fs-extra/issues/32 "update to latest jsonfile [enhancement]" +[#31]: https://github.com/jprichardson/node-fs-extra/issues/31 "Add ensure methods [enhancement]" +[#30]: https://github.com/jprichardson/node-fs-extra/issues/30 "update package.json optional dep `graceful-fs`" +[#29]: https://github.com/jprichardson/node-fs-extra/issues/29 "Copy failing if dest directory doesn't exist. Is this intended?" +[#28]: https://github.com/jprichardson/node-fs-extra/issues/28 "homepage field must be a string url. Deleted." +[#27]: https://github.com/jprichardson/node-fs-extra/issues/27 "Update Readme" +[#26]: https://github.com/jprichardson/node-fs-extra/issues/26 "Add readdir recursive method. [enhancement]" +[#25]: https://github.com/jprichardson/node-fs-extra/pull/25 "adding an `.npmignore` file" +[#24]: https://github.com/jprichardson/node-fs-extra/issues/24 "[bug] cannot run in strict mode [bug]" +[#23]: https://github.com/jprichardson/node-fs-extra/issues/23 "`writeJSON()` should create parent directories" +[#22]: https://github.com/jprichardson/node-fs-extra/pull/22 "Add a limit option to mkdirs()" +[#21]: https://github.com/jprichardson/node-fs-extra/issues/21 "touch() in 0.10.0" +[#20]: https://github.com/jprichardson/node-fs-extra/issues/20 "fs.remove yields callback before directory is really deleted" +[#19]: https://github.com/jprichardson/node-fs-extra/issues/19 "fs.copy err is empty array" +[#18]: https://github.com/jprichardson/node-fs-extra/pull/18 "Exposed copyFile Function" +[#17]: https://github.com/jprichardson/node-fs-extra/issues/17 "Use `require('graceful-fs')` if found instead of `require('fs')`" +[#16]: https://github.com/jprichardson/node-fs-extra/pull/16 "Update README.md" +[#15]: https://github.com/jprichardson/node-fs-extra/issues/15 "Implement cp -r but sync aka copySync. [enhancement]" +[#14]: https://github.com/jprichardson/node-fs-extra/issues/14 "fs.mkdirSync is broken in 0.3.1" +[#13]: https://github.com/jprichardson/node-fs-extra/issues/13 "Thoughts on including a directory tree / file watcher? [enhancement, question]" +[#12]: https://github.com/jprichardson/node-fs-extra/issues/12 "copyFile & copyFileSync are global" +[#11]: https://github.com/jprichardson/node-fs-extra/issues/11 "Thoughts on including a file walker? [enhancement, question]" +[#10]: https://github.com/jprichardson/node-fs-extra/issues/10 "move / moveFile API [enhancement]" +[#9]: https://github.com/jprichardson/node-fs-extra/issues/9 "don't import normal fs stuff into fs-extra" +[#8]: https://github.com/jprichardson/node-fs-extra/pull/8 "Update rimraf to latest version" +[#6]: https://github.com/jprichardson/node-fs-extra/issues/6 "Remove CoffeeScript development dependency" +[#5]: https://github.com/jprichardson/node-fs-extra/issues/5 "comments on naming" +[#4]: https://github.com/jprichardson/node-fs-extra/issues/4 "version bump to 0.2" +[#3]: https://github.com/jprichardson/node-fs-extra/pull/3 "Hi! I fixed some code for you!" +[#2]: https://github.com/jprichardson/node-fs-extra/issues/2 "Merge with fs.extra and mkdirp" +[#1]: https://github.com/jprichardson/node-fs-extra/issues/1 "file-extra npm !exist" diff --git a/node_modules/fs-extra/LICENSE b/node_modules/fs-extra/LICENSE new file mode 100644 index 0000000..93546df --- /dev/null +++ b/node_modules/fs-extra/LICENSE @@ -0,0 +1,15 @@ +(The MIT License) + +Copyright (c) 2011-2017 JP Richardson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/fs-extra/README.md b/node_modules/fs-extra/README.md new file mode 100644 index 0000000..070a5e7 --- /dev/null +++ b/node_modules/fs-extra/README.md @@ -0,0 +1,261 @@ +Node.js: fs-extra +================= + +`fs-extra` adds file system methods that aren't included in the native `fs` module and adds promise support to the `fs` methods. It also uses [`graceful-fs`](https://github.com/isaacs/node-graceful-fs) to prevent `EMFILE` errors. It should be a drop in replacement for `fs`. + +[![npm Package](https://img.shields.io/npm/v/fs-extra.svg)](https://www.npmjs.org/package/fs-extra) +[![License](https://img.shields.io/npm/l/express.svg)](https://github.com/jprichardson/node-fs-extra/blob/master/LICENSE) +[![build status](https://img.shields.io/travis/jprichardson/node-fs-extra/master.svg)](http://travis-ci.org/jprichardson/node-fs-extra) +[![windows Build status](https://img.shields.io/appveyor/ci/jprichardson/node-fs-extra/master.svg?label=windows%20build)](https://ci.appveyor.com/project/jprichardson/node-fs-extra/branch/master) +[![downloads per month](http://img.shields.io/npm/dm/fs-extra.svg)](https://www.npmjs.org/package/fs-extra) +[![Coverage Status](https://img.shields.io/coveralls/github/jprichardson/node-fs-extra/master.svg)](https://coveralls.io/github/jprichardson/node-fs-extra) +[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) + +Why? +---- + +I got tired of including `mkdirp`, `rimraf`, and `ncp` in most of my projects. + + + + +Installation +------------ + + npm install fs-extra + + + +Usage +----- + +`fs-extra` is a drop in replacement for native `fs`. All methods in `fs` are attached to `fs-extra`. All `fs` methods return promises if the callback isn't passed. + +You don't ever need to include the original `fs` module again: + +```js +const fs = require('fs') // this is no longer necessary +``` + +you can now do this: + +```js +const fs = require('fs-extra') +``` + +or if you prefer to make it clear that you're using `fs-extra` and not `fs`, you may want +to name your `fs` variable `fse` like so: + +```js +const fse = require('fs-extra') +``` + +you can also keep both, but it's redundant: + +```js +const fs = require('fs') +const fse = require('fs-extra') +``` + +Sync vs Async vs Async/Await +------------- +Most methods are async by default. All async methods will return a promise if the callback isn't passed. + +Sync methods on the other hand will throw if an error occurs. + +Also Async/Await will throw an error if one occurs. + +Example: + +```js +const fs = require('fs-extra') + +// Async with promises: +fs.copy('/tmp/myfile', '/tmp/mynewfile') + .then(() => console.log('success!')) + .catch(err => console.error(err)) + +// Async with callbacks: +fs.copy('/tmp/myfile', '/tmp/mynewfile', err => { + if (err) return console.error(err) + console.log('success!') +}) + +// Sync: +try { + fs.copySync('/tmp/myfile', '/tmp/mynewfile') + console.log('success!') +} catch (err) { + console.error(err) +} + +// Async/Await: +async function copyFiles () { + try { + await fs.copy('/tmp/myfile', '/tmp/mynewfile') + console.log('success!') + } catch (err) { + console.error(err) + } +} + +copyFiles() +``` + + +Methods +------- + +### Async + +- [copy](docs/copy.md) +- [emptyDir](docs/emptyDir.md) +- [ensureFile](docs/ensureFile.md) +- [ensureDir](docs/ensureDir.md) +- [ensureLink](docs/ensureLink.md) +- [ensureSymlink](docs/ensureSymlink.md) +- [mkdirp](docs/ensureDir.md) +- [mkdirs](docs/ensureDir.md) +- [move](docs/move.md) +- [outputFile](docs/outputFile.md) +- [outputJson](docs/outputJson.md) +- [pathExists](docs/pathExists.md) +- [readJson](docs/readJson.md) +- [remove](docs/remove.md) +- [writeJson](docs/writeJson.md) + +### Sync + +- [copySync](docs/copy-sync.md) +- [emptyDirSync](docs/emptyDir-sync.md) +- [ensureFileSync](docs/ensureFile-sync.md) +- [ensureDirSync](docs/ensureDir-sync.md) +- [ensureLinkSync](docs/ensureLink-sync.md) +- [ensureSymlinkSync](docs/ensureSymlink-sync.md) +- [mkdirpSync](docs/ensureDir-sync.md) +- [mkdirsSync](docs/ensureDir-sync.md) +- [moveSync](docs/move-sync.md) +- [outputFileSync](docs/outputFile-sync.md) +- [outputJsonSync](docs/outputJson-sync.md) +- [pathExistsSync](docs/pathExists-sync.md) +- [readJsonSync](docs/readJson-sync.md) +- [removeSync](docs/remove-sync.md) +- [writeJsonSync](docs/writeJson-sync.md) + + +**NOTE:** You can still use the native Node.js methods. They are promisified and copied over to `fs-extra`. See [notes on `fs.read()` & `fs.write()`](docs/fs-read-write.md) + +### What happened to `walk()` and `walkSync()`? + +They were removed from `fs-extra` in v2.0.0. If you need the functionality, `walk` and `walkSync` are available as separate packages, [`klaw`](https://github.com/jprichardson/node-klaw) and [`klaw-sync`](https://github.com/manidlou/node-klaw-sync). + + +Third Party +----------- + + +### TypeScript + +If you like TypeScript, you can use `fs-extra` with it: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/fs-extra + + +### File / Directory Watching + +If you want to watch for changes to files or directories, then you should use [chokidar](https://github.com/paulmillr/chokidar). + +### Obtain Filesystem (Devices, Partitions) Information + +[fs-filesystem](https://github.com/arthurintelligence/node-fs-filesystem) allows you to read the state of the filesystem of the host on which it is run. It returns information about both the devices and the partitions (volumes) of the system. + +### Misc. + +- [fs-extra-debug](https://github.com/jdxcode/fs-extra-debug) - Send your fs-extra calls to [debug](https://npmjs.org/package/debug). +- [mfs](https://github.com/cadorn/mfs) - Monitor your fs-extra calls. + + + +Hacking on fs-extra +------------------- + +Wanna hack on `fs-extra`? Great! Your help is needed! [fs-extra is one of the most depended upon Node.js packages](http://nodei.co/npm/fs-extra.png?downloads=true&downloadRank=true&stars=true). This project +uses [JavaScript Standard Style](https://github.com/feross/standard) - if the name or style choices bother you, +you're gonna have to get over it :) If `standard` is good enough for `npm`, it's good enough for `fs-extra`. + +[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) + +What's needed? +- First, take a look at existing issues. Those are probably going to be where the priority lies. +- More tests for edge cases. Specifically on different platforms. There can never be enough tests. +- Improve test coverage. See coveralls output for more info. + +Note: If you make any big changes, **you should definitely file an issue for discussion first.** + +### Running the Test Suite + +fs-extra contains hundreds of tests. + +- `npm run lint`: runs the linter ([standard](http://standardjs.com/)) +- `npm run unit`: runs the unit tests +- `npm test`: runs both the linter and the tests + + +### Windows + +If you run the tests on the Windows and receive a lot of symbolic link `EPERM` permission errors, it's +because on Windows you need elevated privilege to create symbolic links. You can add this to your Windows's +account by following the instructions here: http://superuser.com/questions/104845/permission-to-make-symbolic-links-in-windows-7 +However, I didn't have much luck doing this. + +Since I develop on Mac OS X, I use VMWare Fusion for Windows testing. I create a shared folder that I map to a drive on Windows. +I open the `Node.js command prompt` and run as `Administrator`. I then map the network drive running the following command: + + net use z: "\\vmware-host\Shared Folders" + +I can then navigate to my `fs-extra` directory and run the tests. + + +Naming +------ + +I put a lot of thought into the naming of these functions. Inspired by @coolaj86's request. So he deserves much of the credit for raising the issue. See discussion(s) here: + +* https://github.com/jprichardson/node-fs-extra/issues/2 +* https://github.com/flatiron/utile/issues/11 +* https://github.com/ryanmcgrath/wrench-js/issues/29 +* https://github.com/substack/node-mkdirp/issues/17 + +First, I believe that in as many cases as possible, the [Node.js naming schemes](http://nodejs.org/api/fs.html) should be chosen. However, there are problems with the Node.js own naming schemes. + +For example, `fs.readFile()` and `fs.readdir()`: the **F** is capitalized in *File* and the **d** is not capitalized in *dir*. Perhaps a bit pedantic, but they should still be consistent. Also, Node.js has chosen a lot of POSIX naming schemes, which I believe is great. See: `fs.mkdir()`, `fs.rmdir()`, `fs.chown()`, etc. + +We have a dilemma though. How do you consistently name methods that perform the following POSIX commands: `cp`, `cp -r`, `mkdir -p`, and `rm -rf`? + +My perspective: when in doubt, err on the side of simplicity. A directory is just a hierarchical grouping of directories and files. Consider that for a moment. So when you want to copy it or remove it, in most cases you'll want to copy or remove all of its contents. When you want to create a directory, if the directory that it's suppose to be contained in does not exist, then in most cases you'll want to create that too. + +So, if you want to remove a file or a directory regardless of whether it has contents, just call `fs.remove(path)`. If you want to copy a file or a directory whether it has contents, just call `fs.copy(source, destination)`. If you want to create a directory regardless of whether its parent directories exist, just call `fs.mkdirs(path)` or `fs.mkdirp(path)`. + + +Credit +------ + +`fs-extra` wouldn't be possible without using the modules from the following authors: + +- [Isaac Shlueter](https://github.com/isaacs) +- [Charlie McConnel](https://github.com/avianflu) +- [James Halliday](https://github.com/substack) +- [Andrew Kelley](https://github.com/andrewrk) + + + + +License +------- + +Licensed under MIT + +Copyright (c) 2011-2017 [JP Richardson](https://github.com/jprichardson) + +[1]: http://nodejs.org/docs/latest/api/fs.html + + +[jsonfile]: https://github.com/jprichardson/node-jsonfile diff --git a/node_modules/fs-extra/lib/copy-sync/copy-sync.js b/node_modules/fs-extra/lib/copy-sync/copy-sync.js new file mode 100644 index 0000000..f2831b3 --- /dev/null +++ b/node_modules/fs-extra/lib/copy-sync/copy-sync.js @@ -0,0 +1,164 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const mkdirpSync = require('../mkdirs').mkdirsSync +const utimesSync = require('../util/utimes.js').utimesMillisSync +const stat = require('../util/stat') + +function copySync (src, dest, opts) { + if (typeof opts === 'function') { + opts = { filter: opts } + } + + opts = opts || {} + opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now + opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber + + // Warn about using preserveTimestamps on 32-bit node + if (opts.preserveTimestamps && process.arch === 'ia32') { + console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n + see https://github.com/jprichardson/node-fs-extra/issues/269`) + } + + const { srcStat, destStat } = stat.checkPathsSync(src, dest, 'copy') + stat.checkParentPathsSync(src, srcStat, dest, 'copy') + return handleFilterAndCopy(destStat, src, dest, opts) +} + +function handleFilterAndCopy (destStat, src, dest, opts) { + if (opts.filter && !opts.filter(src, dest)) return + const destParent = path.dirname(dest) + if (!fs.existsSync(destParent)) mkdirpSync(destParent) + return startCopy(destStat, src, dest, opts) +} + +function startCopy (destStat, src, dest, opts) { + if (opts.filter && !opts.filter(src, dest)) return + return getStats(destStat, src, dest, opts) +} + +function getStats (destStat, src, dest, opts) { + const statSync = opts.dereference ? fs.statSync : fs.lstatSync + const srcStat = statSync(src) + + if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts) + else if (srcStat.isFile() || + srcStat.isCharacterDevice() || + srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts) + else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts) +} + +function onFile (srcStat, destStat, src, dest, opts) { + if (!destStat) return copyFile(srcStat, src, dest, opts) + return mayCopyFile(srcStat, src, dest, opts) +} + +function mayCopyFile (srcStat, src, dest, opts) { + if (opts.overwrite) { + fs.unlinkSync(dest) + return copyFile(srcStat, src, dest, opts) + } else if (opts.errorOnExist) { + throw new Error(`'${dest}' already exists`) + } +} + +function copyFile (srcStat, src, dest, opts) { + if (typeof fs.copyFileSync === 'function') { + fs.copyFileSync(src, dest) + fs.chmodSync(dest, srcStat.mode) + if (opts.preserveTimestamps) { + return utimesSync(dest, srcStat.atime, srcStat.mtime) + } + return + } + return copyFileFallback(srcStat, src, dest, opts) +} + +function copyFileFallback (srcStat, src, dest, opts) { + const BUF_LENGTH = 64 * 1024 + const _buff = require('../util/buffer')(BUF_LENGTH) + + const fdr = fs.openSync(src, 'r') + const fdw = fs.openSync(dest, 'w', srcStat.mode) + let pos = 0 + + while (pos < srcStat.size) { + const bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos) + fs.writeSync(fdw, _buff, 0, bytesRead) + pos += bytesRead + } + + if (opts.preserveTimestamps) fs.futimesSync(fdw, srcStat.atime, srcStat.mtime) + + fs.closeSync(fdr) + fs.closeSync(fdw) +} + +function onDir (srcStat, destStat, src, dest, opts) { + if (!destStat) return mkDirAndCopy(srcStat, src, dest, opts) + if (destStat && !destStat.isDirectory()) { + throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`) + } + return copyDir(src, dest, opts) +} + +function mkDirAndCopy (srcStat, src, dest, opts) { + fs.mkdirSync(dest) + copyDir(src, dest, opts) + return fs.chmodSync(dest, srcStat.mode) +} + +function copyDir (src, dest, opts) { + fs.readdirSync(src).forEach(item => copyDirItem(item, src, dest, opts)) +} + +function copyDirItem (item, src, dest, opts) { + const srcItem = path.join(src, item) + const destItem = path.join(dest, item) + const { destStat } = stat.checkPathsSync(srcItem, destItem, 'copy') + return startCopy(destStat, srcItem, destItem, opts) +} + +function onLink (destStat, src, dest, opts) { + let resolvedSrc = fs.readlinkSync(src) + if (opts.dereference) { + resolvedSrc = path.resolve(process.cwd(), resolvedSrc) + } + + if (!destStat) { + return fs.symlinkSync(resolvedSrc, dest) + } else { + let resolvedDest + try { + resolvedDest = fs.readlinkSync(dest) + } catch (err) { + // dest exists and is a regular file or directory, + // Windows may throw UNKNOWN error. If dest already exists, + // fs throws error anyway, so no need to guard against it here. + if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlinkSync(resolvedSrc, dest) + throw err + } + if (opts.dereference) { + resolvedDest = path.resolve(process.cwd(), resolvedDest) + } + if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) { + throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`) + } + + // prevent copy if src is a subdir of dest since unlinking + // dest in this case would result in removing src contents + // and therefore a broken symlink would be created. + if (fs.statSync(dest).isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) { + throw new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`) + } + return copyLink(resolvedSrc, dest) + } +} + +function copyLink (resolvedSrc, dest) { + fs.unlinkSync(dest) + return fs.symlinkSync(resolvedSrc, dest) +} + +module.exports = copySync diff --git a/node_modules/fs-extra/lib/copy-sync/index.js b/node_modules/fs-extra/lib/copy-sync/index.js new file mode 100644 index 0000000..65945ae --- /dev/null +++ b/node_modules/fs-extra/lib/copy-sync/index.js @@ -0,0 +1,5 @@ +'use strict' + +module.exports = { + copySync: require('./copy-sync') +} diff --git a/node_modules/fs-extra/lib/copy/copy.js b/node_modules/fs-extra/lib/copy/copy.js new file mode 100644 index 0000000..411fc64 --- /dev/null +++ b/node_modules/fs-extra/lib/copy/copy.js @@ -0,0 +1,212 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const mkdirp = require('../mkdirs').mkdirs +const pathExists = require('../path-exists').pathExists +const utimes = require('../util/utimes').utimesMillis +const stat = require('../util/stat') + +function copy (src, dest, opts, cb) { + if (typeof opts === 'function' && !cb) { + cb = opts + opts = {} + } else if (typeof opts === 'function') { + opts = { filter: opts } + } + + cb = cb || function () {} + opts = opts || {} + + opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now + opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber + + // Warn about using preserveTimestamps on 32-bit node + if (opts.preserveTimestamps && process.arch === 'ia32') { + console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n + see https://github.com/jprichardson/node-fs-extra/issues/269`) + } + + stat.checkPaths(src, dest, 'copy', (err, stats) => { + if (err) return cb(err) + const { srcStat, destStat } = stats + stat.checkParentPaths(src, srcStat, dest, 'copy', err => { + if (err) return cb(err) + if (opts.filter) return handleFilter(checkParentDir, destStat, src, dest, opts, cb) + return checkParentDir(destStat, src, dest, opts, cb) + }) + }) +} + +function checkParentDir (destStat, src, dest, opts, cb) { + const destParent = path.dirname(dest) + pathExists(destParent, (err, dirExists) => { + if (err) return cb(err) + if (dirExists) return startCopy(destStat, src, dest, opts, cb) + mkdirp(destParent, err => { + if (err) return cb(err) + return startCopy(destStat, src, dest, opts, cb) + }) + }) +} + +function handleFilter (onInclude, destStat, src, dest, opts, cb) { + Promise.resolve(opts.filter(src, dest)).then(include => { + if (include) return onInclude(destStat, src, dest, opts, cb) + return cb() + }, error => cb(error)) +} + +function startCopy (destStat, src, dest, opts, cb) { + if (opts.filter) return handleFilter(getStats, destStat, src, dest, opts, cb) + return getStats(destStat, src, dest, opts, cb) +} + +function getStats (destStat, src, dest, opts, cb) { + const stat = opts.dereference ? fs.stat : fs.lstat + stat(src, (err, srcStat) => { + if (err) return cb(err) + + if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts, cb) + else if (srcStat.isFile() || + srcStat.isCharacterDevice() || + srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts, cb) + else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts, cb) + }) +} + +function onFile (srcStat, destStat, src, dest, opts, cb) { + if (!destStat) return copyFile(srcStat, src, dest, opts, cb) + return mayCopyFile(srcStat, src, dest, opts, cb) +} + +function mayCopyFile (srcStat, src, dest, opts, cb) { + if (opts.overwrite) { + fs.unlink(dest, err => { + if (err) return cb(err) + return copyFile(srcStat, src, dest, opts, cb) + }) + } else if (opts.errorOnExist) { + return cb(new Error(`'${dest}' already exists`)) + } else return cb() +} + +function copyFile (srcStat, src, dest, opts, cb) { + if (typeof fs.copyFile === 'function') { + return fs.copyFile(src, dest, err => { + if (err) return cb(err) + return setDestModeAndTimestamps(srcStat, dest, opts, cb) + }) + } + return copyFileFallback(srcStat, src, dest, opts, cb) +} + +function copyFileFallback (srcStat, src, dest, opts, cb) { + const rs = fs.createReadStream(src) + rs.on('error', err => cb(err)).once('open', () => { + const ws = fs.createWriteStream(dest, { mode: srcStat.mode }) + ws.on('error', err => cb(err)) + .on('open', () => rs.pipe(ws)) + .once('close', () => setDestModeAndTimestamps(srcStat, dest, opts, cb)) + }) +} + +function setDestModeAndTimestamps (srcStat, dest, opts, cb) { + fs.chmod(dest, srcStat.mode, err => { + if (err) return cb(err) + if (opts.preserveTimestamps) { + return utimes(dest, srcStat.atime, srcStat.mtime, cb) + } + return cb() + }) +} + +function onDir (srcStat, destStat, src, dest, opts, cb) { + if (!destStat) return mkDirAndCopy(srcStat, src, dest, opts, cb) + if (destStat && !destStat.isDirectory()) { + return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)) + } + return copyDir(src, dest, opts, cb) +} + +function mkDirAndCopy (srcStat, src, dest, opts, cb) { + fs.mkdir(dest, err => { + if (err) return cb(err) + copyDir(src, dest, opts, err => { + if (err) return cb(err) + return fs.chmod(dest, srcStat.mode, cb) + }) + }) +} + +function copyDir (src, dest, opts, cb) { + fs.readdir(src, (err, items) => { + if (err) return cb(err) + return copyDirItems(items, src, dest, opts, cb) + }) +} + +function copyDirItems (items, src, dest, opts, cb) { + const item = items.pop() + if (!item) return cb() + return copyDirItem(items, item, src, dest, opts, cb) +} + +function copyDirItem (items, item, src, dest, opts, cb) { + const srcItem = path.join(src, item) + const destItem = path.join(dest, item) + stat.checkPaths(srcItem, destItem, 'copy', (err, stats) => { + if (err) return cb(err) + const { destStat } = stats + startCopy(destStat, srcItem, destItem, opts, err => { + if (err) return cb(err) + return copyDirItems(items, src, dest, opts, cb) + }) + }) +} + +function onLink (destStat, src, dest, opts, cb) { + fs.readlink(src, (err, resolvedSrc) => { + if (err) return cb(err) + if (opts.dereference) { + resolvedSrc = path.resolve(process.cwd(), resolvedSrc) + } + + if (!destStat) { + return fs.symlink(resolvedSrc, dest, cb) + } else { + fs.readlink(dest, (err, resolvedDest) => { + if (err) { + // dest exists and is a regular file or directory, + // Windows may throw UNKNOWN error. If dest already exists, + // fs throws error anyway, so no need to guard against it here. + if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlink(resolvedSrc, dest, cb) + return cb(err) + } + if (opts.dereference) { + resolvedDest = path.resolve(process.cwd(), resolvedDest) + } + if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) { + return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)) + } + + // do not copy if src is a subdir of dest since unlinking + // dest in this case would result in removing src contents + // and therefore a broken symlink would be created. + if (destStat.isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) { + return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`)) + } + return copyLink(resolvedSrc, dest, cb) + }) + } + }) +} + +function copyLink (resolvedSrc, dest, cb) { + fs.unlink(dest, err => { + if (err) return cb(err) + return fs.symlink(resolvedSrc, dest, cb) + }) +} + +module.exports = copy diff --git a/node_modules/fs-extra/lib/copy/index.js b/node_modules/fs-extra/lib/copy/index.js new file mode 100644 index 0000000..b7e4f7f --- /dev/null +++ b/node_modules/fs-extra/lib/copy/index.js @@ -0,0 +1,6 @@ +'use strict' + +const u = require('universalify').fromCallback +module.exports = { + copy: u(require('./copy')) +} diff --git a/node_modules/fs-extra/lib/empty/index.js b/node_modules/fs-extra/lib/empty/index.js new file mode 100644 index 0000000..204d53b --- /dev/null +++ b/node_modules/fs-extra/lib/empty/index.js @@ -0,0 +1,48 @@ +'use strict' + +const u = require('universalify').fromCallback +const fs = require('graceful-fs') +const path = require('path') +const mkdir = require('../mkdirs') +const remove = require('../remove') + +const emptyDir = u(function emptyDir (dir, callback) { + callback = callback || function () {} + fs.readdir(dir, (err, items) => { + if (err) return mkdir.mkdirs(dir, callback) + + items = items.map(item => path.join(dir, item)) + + deleteItem() + + function deleteItem () { + const item = items.pop() + if (!item) return callback() + remove.remove(item, err => { + if (err) return callback(err) + deleteItem() + }) + } + }) +}) + +function emptyDirSync (dir) { + let items + try { + items = fs.readdirSync(dir) + } catch (err) { + return mkdir.mkdirsSync(dir) + } + + items.forEach(item => { + item = path.join(dir, item) + remove.removeSync(item) + }) +} + +module.exports = { + emptyDirSync, + emptydirSync: emptyDirSync, + emptyDir, + emptydir: emptyDir +} diff --git a/node_modules/fs-extra/lib/ensure/file.js b/node_modules/fs-extra/lib/ensure/file.js new file mode 100644 index 0000000..67eed30 --- /dev/null +++ b/node_modules/fs-extra/lib/ensure/file.js @@ -0,0 +1,49 @@ +'use strict' + +const u = require('universalify').fromCallback +const path = require('path') +const fs = require('graceful-fs') +const mkdir = require('../mkdirs') +const pathExists = require('../path-exists').pathExists + +function createFile (file, callback) { + function makeFile () { + fs.writeFile(file, '', err => { + if (err) return callback(err) + callback() + }) + } + + fs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err + if (!err && stats.isFile()) return callback() + const dir = path.dirname(file) + pathExists(dir, (err, dirExists) => { + if (err) return callback(err) + if (dirExists) return makeFile() + mkdir.mkdirs(dir, err => { + if (err) return callback(err) + makeFile() + }) + }) + }) +} + +function createFileSync (file) { + let stats + try { + stats = fs.statSync(file) + } catch (e) {} + if (stats && stats.isFile()) return + + const dir = path.dirname(file) + if (!fs.existsSync(dir)) { + mkdir.mkdirsSync(dir) + } + + fs.writeFileSync(file, '') +} + +module.exports = { + createFile: u(createFile), + createFileSync +} diff --git a/node_modules/fs-extra/lib/ensure/index.js b/node_modules/fs-extra/lib/ensure/index.js new file mode 100644 index 0000000..c1f67b7 --- /dev/null +++ b/node_modules/fs-extra/lib/ensure/index.js @@ -0,0 +1,23 @@ +'use strict' + +const file = require('./file') +const link = require('./link') +const symlink = require('./symlink') + +module.exports = { + // file + createFile: file.createFile, + createFileSync: file.createFileSync, + ensureFile: file.createFile, + ensureFileSync: file.createFileSync, + // link + createLink: link.createLink, + createLinkSync: link.createLinkSync, + ensureLink: link.createLink, + ensureLinkSync: link.createLinkSync, + // symlink + createSymlink: symlink.createSymlink, + createSymlinkSync: symlink.createSymlinkSync, + ensureSymlink: symlink.createSymlink, + ensureSymlinkSync: symlink.createSymlinkSync +} diff --git a/node_modules/fs-extra/lib/ensure/link.js b/node_modules/fs-extra/lib/ensure/link.js new file mode 100644 index 0000000..2cd4196 --- /dev/null +++ b/node_modules/fs-extra/lib/ensure/link.js @@ -0,0 +1,61 @@ +'use strict' + +const u = require('universalify').fromCallback +const path = require('path') +const fs = require('graceful-fs') +const mkdir = require('../mkdirs') +const pathExists = require('../path-exists').pathExists + +function createLink (srcpath, dstpath, callback) { + function makeLink (srcpath, dstpath) { + fs.link(srcpath, dstpath, err => { + if (err) return callback(err) + callback(null) + }) + } + + pathExists(dstpath, (err, destinationExists) => { + if (err) return callback(err) + if (destinationExists) return callback(null) + fs.lstat(srcpath, (err) => { + if (err) { + err.message = err.message.replace('lstat', 'ensureLink') + return callback(err) + } + + const dir = path.dirname(dstpath) + pathExists(dir, (err, dirExists) => { + if (err) return callback(err) + if (dirExists) return makeLink(srcpath, dstpath) + mkdir.mkdirs(dir, err => { + if (err) return callback(err) + makeLink(srcpath, dstpath) + }) + }) + }) + }) +} + +function createLinkSync (srcpath, dstpath) { + const destinationExists = fs.existsSync(dstpath) + if (destinationExists) return undefined + + try { + fs.lstatSync(srcpath) + } catch (err) { + err.message = err.message.replace('lstat', 'ensureLink') + throw err + } + + const dir = path.dirname(dstpath) + const dirExists = fs.existsSync(dir) + if (dirExists) return fs.linkSync(srcpath, dstpath) + mkdir.mkdirsSync(dir) + + return fs.linkSync(srcpath, dstpath) +} + +module.exports = { + createLink: u(createLink), + createLinkSync +} diff --git a/node_modules/fs-extra/lib/ensure/symlink-paths.js b/node_modules/fs-extra/lib/ensure/symlink-paths.js new file mode 100644 index 0000000..24e7e1c --- /dev/null +++ b/node_modules/fs-extra/lib/ensure/symlink-paths.js @@ -0,0 +1,99 @@ +'use strict' + +const path = require('path') +const fs = require('graceful-fs') +const pathExists = require('../path-exists').pathExists + +/** + * Function that returns two types of paths, one relative to symlink, and one + * relative to the current working directory. Checks if path is absolute or + * relative. If the path is relative, this function checks if the path is + * relative to symlink or relative to current working directory. This is an + * initiative to find a smarter `srcpath` to supply when building symlinks. + * This allows you to determine which path to use out of one of three possible + * types of source paths. The first is an absolute path. This is detected by + * `path.isAbsolute()`. When an absolute path is provided, it is checked to + * see if it exists. If it does it's used, if not an error is returned + * (callback)/ thrown (sync). The other two options for `srcpath` are a + * relative url. By default Node's `fs.symlink` works by creating a symlink + * using `dstpath` and expects the `srcpath` to be relative to the newly + * created symlink. If you provide a `srcpath` that does not exist on the file + * system it results in a broken symlink. To minimize this, the function + * checks to see if the 'relative to symlink' source file exists, and if it + * does it will use it. If it does not, it checks if there's a file that + * exists that is relative to the current working directory, if does its used. + * This preserves the expectations of the original fs.symlink spec and adds + * the ability to pass in `relative to current working direcotry` paths. + */ + +function symlinkPaths (srcpath, dstpath, callback) { + if (path.isAbsolute(srcpath)) { + return fs.lstat(srcpath, (err) => { + if (err) { + err.message = err.message.replace('lstat', 'ensureSymlink') + return callback(err) + } + return callback(null, { + 'toCwd': srcpath, + 'toDst': srcpath + }) + }) + } else { + const dstdir = path.dirname(dstpath) + const relativeToDst = path.join(dstdir, srcpath) + return pathExists(relativeToDst, (err, exists) => { + if (err) return callback(err) + if (exists) { + return callback(null, { + 'toCwd': relativeToDst, + 'toDst': srcpath + }) + } else { + return fs.lstat(srcpath, (err) => { + if (err) { + err.message = err.message.replace('lstat', 'ensureSymlink') + return callback(err) + } + return callback(null, { + 'toCwd': srcpath, + 'toDst': path.relative(dstdir, srcpath) + }) + }) + } + }) + } +} + +function symlinkPathsSync (srcpath, dstpath) { + let exists + if (path.isAbsolute(srcpath)) { + exists = fs.existsSync(srcpath) + if (!exists) throw new Error('absolute srcpath does not exist') + return { + 'toCwd': srcpath, + 'toDst': srcpath + } + } else { + const dstdir = path.dirname(dstpath) + const relativeToDst = path.join(dstdir, srcpath) + exists = fs.existsSync(relativeToDst) + if (exists) { + return { + 'toCwd': relativeToDst, + 'toDst': srcpath + } + } else { + exists = fs.existsSync(srcpath) + if (!exists) throw new Error('relative srcpath does not exist') + return { + 'toCwd': srcpath, + 'toDst': path.relative(dstdir, srcpath) + } + } + } +} + +module.exports = { + symlinkPaths, + symlinkPathsSync +} diff --git a/node_modules/fs-extra/lib/ensure/symlink-type.js b/node_modules/fs-extra/lib/ensure/symlink-type.js new file mode 100644 index 0000000..4f8787c --- /dev/null +++ b/node_modules/fs-extra/lib/ensure/symlink-type.js @@ -0,0 +1,31 @@ +'use strict' + +const fs = require('graceful-fs') + +function symlinkType (srcpath, type, callback) { + callback = (typeof type === 'function') ? type : callback + type = (typeof type === 'function') ? false : type + if (type) return callback(null, type) + fs.lstat(srcpath, (err, stats) => { + if (err) return callback(null, 'file') + type = (stats && stats.isDirectory()) ? 'dir' : 'file' + callback(null, type) + }) +} + +function symlinkTypeSync (srcpath, type) { + let stats + + if (type) return type + try { + stats = fs.lstatSync(srcpath) + } catch (e) { + return 'file' + } + return (stats && stats.isDirectory()) ? 'dir' : 'file' +} + +module.exports = { + symlinkType, + symlinkTypeSync +} diff --git a/node_modules/fs-extra/lib/ensure/symlink.js b/node_modules/fs-extra/lib/ensure/symlink.js new file mode 100644 index 0000000..fe68b79 --- /dev/null +++ b/node_modules/fs-extra/lib/ensure/symlink.js @@ -0,0 +1,63 @@ +'use strict' + +const u = require('universalify').fromCallback +const path = require('path') +const fs = require('graceful-fs') +const _mkdirs = require('../mkdirs') +const mkdirs = _mkdirs.mkdirs +const mkdirsSync = _mkdirs.mkdirsSync + +const _symlinkPaths = require('./symlink-paths') +const symlinkPaths = _symlinkPaths.symlinkPaths +const symlinkPathsSync = _symlinkPaths.symlinkPathsSync + +const _symlinkType = require('./symlink-type') +const symlinkType = _symlinkType.symlinkType +const symlinkTypeSync = _symlinkType.symlinkTypeSync + +const pathExists = require('../path-exists').pathExists + +function createSymlink (srcpath, dstpath, type, callback) { + callback = (typeof type === 'function') ? type : callback + type = (typeof type === 'function') ? false : type + + pathExists(dstpath, (err, destinationExists) => { + if (err) return callback(err) + if (destinationExists) return callback(null) + symlinkPaths(srcpath, dstpath, (err, relative) => { + if (err) return callback(err) + srcpath = relative.toDst + symlinkType(relative.toCwd, type, (err, type) => { + if (err) return callback(err) + const dir = path.dirname(dstpath) + pathExists(dir, (err, dirExists) => { + if (err) return callback(err) + if (dirExists) return fs.symlink(srcpath, dstpath, type, callback) + mkdirs(dir, err => { + if (err) return callback(err) + fs.symlink(srcpath, dstpath, type, callback) + }) + }) + }) + }) + }) +} + +function createSymlinkSync (srcpath, dstpath, type) { + const destinationExists = fs.existsSync(dstpath) + if (destinationExists) return undefined + + const relative = symlinkPathsSync(srcpath, dstpath) + srcpath = relative.toDst + type = symlinkTypeSync(relative.toCwd, type) + const dir = path.dirname(dstpath) + const exists = fs.existsSync(dir) + if (exists) return fs.symlinkSync(srcpath, dstpath, type) + mkdirsSync(dir) + return fs.symlinkSync(srcpath, dstpath, type) +} + +module.exports = { + createSymlink: u(createSymlink), + createSymlinkSync +} diff --git a/node_modules/fs-extra/lib/fs/index.js b/node_modules/fs-extra/lib/fs/index.js new file mode 100644 index 0000000..a7b2292 --- /dev/null +++ b/node_modules/fs-extra/lib/fs/index.js @@ -0,0 +1,109 @@ +'use strict' +// This is adapted from https://github.com/normalize/mz +// Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors +const u = require('universalify').fromCallback +const fs = require('graceful-fs') + +const api = [ + 'access', + 'appendFile', + 'chmod', + 'chown', + 'close', + 'copyFile', + 'fchmod', + 'fchown', + 'fdatasync', + 'fstat', + 'fsync', + 'ftruncate', + 'futimes', + 'lchown', + 'lchmod', + 'link', + 'lstat', + 'mkdir', + 'mkdtemp', + 'open', + 'readFile', + 'readdir', + 'readlink', + 'realpath', + 'rename', + 'rmdir', + 'stat', + 'symlink', + 'truncate', + 'unlink', + 'utimes', + 'writeFile' +].filter(key => { + // Some commands are not available on some systems. Ex: + // fs.copyFile was added in Node.js v8.5.0 + // fs.mkdtemp was added in Node.js v5.10.0 + // fs.lchown is not available on at least some Linux + return typeof fs[key] === 'function' +}) + +// Export all keys: +Object.keys(fs).forEach(key => { + if (key === 'promises') { + // fs.promises is a getter property that triggers ExperimentalWarning + // Don't re-export it here, the getter is defined in "lib/index.js" + return + } + exports[key] = fs[key] +}) + +// Universalify async methods: +api.forEach(method => { + exports[method] = u(fs[method]) +}) + +// We differ from mz/fs in that we still ship the old, broken, fs.exists() +// since we are a drop-in replacement for the native module +exports.exists = function (filename, callback) { + if (typeof callback === 'function') { + return fs.exists(filename, callback) + } + return new Promise(resolve => { + return fs.exists(filename, resolve) + }) +} + +// fs.read() & fs.write need special treatment due to multiple callback args + +exports.read = function (fd, buffer, offset, length, position, callback) { + if (typeof callback === 'function') { + return fs.read(fd, buffer, offset, length, position, callback) + } + return new Promise((resolve, reject) => { + fs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => { + if (err) return reject(err) + resolve({ bytesRead, buffer }) + }) + }) +} + +// Function signature can be +// fs.write(fd, buffer[, offset[, length[, position]]], callback) +// OR +// fs.write(fd, string[, position[, encoding]], callback) +// We need to handle both cases, so we use ...args +exports.write = function (fd, buffer, ...args) { + if (typeof args[args.length - 1] === 'function') { + return fs.write(fd, buffer, ...args) + } + + return new Promise((resolve, reject) => { + fs.write(fd, buffer, ...args, (err, bytesWritten, buffer) => { + if (err) return reject(err) + resolve({ bytesWritten, buffer }) + }) + }) +} + +// fs.realpath.native only available in Node v9.2+ +if (typeof fs.realpath.native === 'function') { + exports.realpath.native = u(fs.realpath.native) +} diff --git a/node_modules/fs-extra/lib/index.js b/node_modules/fs-extra/lib/index.js new file mode 100644 index 0000000..40e37b1 --- /dev/null +++ b/node_modules/fs-extra/lib/index.js @@ -0,0 +1,28 @@ +'use strict' + +module.exports = Object.assign( + {}, + // Export promiseified graceful-fs: + require('./fs'), + // Export extra methods: + require('./copy-sync'), + require('./copy'), + require('./empty'), + require('./ensure'), + require('./json'), + require('./mkdirs'), + require('./move-sync'), + require('./move'), + require('./output'), + require('./path-exists'), + require('./remove') +) + +// Export fs.promises as a getter property so that we don't trigger +// ExperimentalWarning before fs.promises is actually accessed. +const fs = require('fs') +if (Object.getOwnPropertyDescriptor(fs, 'promises')) { + Object.defineProperty(module.exports, 'promises', { + get () { return fs.promises } + }) +} diff --git a/node_modules/fs-extra/lib/json/index.js b/node_modules/fs-extra/lib/json/index.js new file mode 100644 index 0000000..bae68d4 --- /dev/null +++ b/node_modules/fs-extra/lib/json/index.js @@ -0,0 +1,16 @@ +'use strict' + +const u = require('universalify').fromCallback +const jsonFile = require('./jsonfile') + +jsonFile.outputJson = u(require('./output-json')) +jsonFile.outputJsonSync = require('./output-json-sync') +// aliases +jsonFile.outputJSON = jsonFile.outputJson +jsonFile.outputJSONSync = jsonFile.outputJsonSync +jsonFile.writeJSON = jsonFile.writeJson +jsonFile.writeJSONSync = jsonFile.writeJsonSync +jsonFile.readJSON = jsonFile.readJson +jsonFile.readJSONSync = jsonFile.readJsonSync + +module.exports = jsonFile diff --git a/node_modules/fs-extra/lib/json/jsonfile.js b/node_modules/fs-extra/lib/json/jsonfile.js new file mode 100644 index 0000000..59cdb3e --- /dev/null +++ b/node_modules/fs-extra/lib/json/jsonfile.js @@ -0,0 +1,12 @@ +'use strict' + +const u = require('universalify').fromCallback +const jsonFile = require('jsonfile') + +module.exports = { + // jsonfile exports + readJson: u(jsonFile.readFile), + readJsonSync: jsonFile.readFileSync, + writeJson: u(jsonFile.writeFile), + writeJsonSync: jsonFile.writeFileSync +} diff --git a/node_modules/fs-extra/lib/json/output-json-sync.js b/node_modules/fs-extra/lib/json/output-json-sync.js new file mode 100644 index 0000000..6f76710 --- /dev/null +++ b/node_modules/fs-extra/lib/json/output-json-sync.js @@ -0,0 +1,18 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const mkdir = require('../mkdirs') +const jsonFile = require('./jsonfile') + +function outputJsonSync (file, data, options) { + const dir = path.dirname(file) + + if (!fs.existsSync(dir)) { + mkdir.mkdirsSync(dir) + } + + jsonFile.writeJsonSync(file, data, options) +} + +module.exports = outputJsonSync diff --git a/node_modules/fs-extra/lib/json/output-json.js b/node_modules/fs-extra/lib/json/output-json.js new file mode 100644 index 0000000..d45edb8 --- /dev/null +++ b/node_modules/fs-extra/lib/json/output-json.js @@ -0,0 +1,27 @@ +'use strict' + +const path = require('path') +const mkdir = require('../mkdirs') +const pathExists = require('../path-exists').pathExists +const jsonFile = require('./jsonfile') + +function outputJson (file, data, options, callback) { + if (typeof options === 'function') { + callback = options + options = {} + } + + const dir = path.dirname(file) + + pathExists(dir, (err, itDoes) => { + if (err) return callback(err) + if (itDoes) return jsonFile.writeJson(file, data, options, callback) + + mkdir.mkdirs(dir, err => { + if (err) return callback(err) + jsonFile.writeJson(file, data, options, callback) + }) + }) +} + +module.exports = outputJson diff --git a/node_modules/fs-extra/lib/mkdirs/index.js b/node_modules/fs-extra/lib/mkdirs/index.js new file mode 100644 index 0000000..d6e7e5b --- /dev/null +++ b/node_modules/fs-extra/lib/mkdirs/index.js @@ -0,0 +1,14 @@ +'use strict' +const u = require('universalify').fromCallback +const mkdirs = u(require('./mkdirs')) +const mkdirsSync = require('./mkdirs-sync') + +module.exports = { + mkdirs, + mkdirsSync, + // alias + mkdirp: mkdirs, + mkdirpSync: mkdirsSync, + ensureDir: mkdirs, + ensureDirSync: mkdirsSync +} diff --git a/node_modules/fs-extra/lib/mkdirs/mkdirs-sync.js b/node_modules/fs-extra/lib/mkdirs/mkdirs-sync.js new file mode 100644 index 0000000..a34acb9 --- /dev/null +++ b/node_modules/fs-extra/lib/mkdirs/mkdirs-sync.js @@ -0,0 +1,54 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const invalidWin32Path = require('./win32').invalidWin32Path + +const o777 = parseInt('0777', 8) + +function mkdirsSync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts } + } + + let mode = opts.mode + const xfs = opts.fs || fs + + if (process.platform === 'win32' && invalidWin32Path(p)) { + const errInval = new Error(p + ' contains invalid WIN32 path characters.') + errInval.code = 'EINVAL' + throw errInval + } + + if (mode === undefined) { + mode = o777 & (~process.umask()) + } + if (!made) made = null + + p = path.resolve(p) + + try { + xfs.mkdirSync(p, mode) + made = made || p + } catch (err0) { + if (err0.code === 'ENOENT') { + if (path.dirname(p) === p) throw err0 + made = mkdirsSync(path.dirname(p), opts, made) + mkdirsSync(p, opts, made) + } else { + // In the case of any other error, just see if there's a dir there + // already. If so, then hooray! If not, then something is borked. + let stat + try { + stat = xfs.statSync(p) + } catch (err1) { + throw err0 + } + if (!stat.isDirectory()) throw err0 + } + } + + return made +} + +module.exports = mkdirsSync diff --git a/node_modules/fs-extra/lib/mkdirs/mkdirs.js b/node_modules/fs-extra/lib/mkdirs/mkdirs.js new file mode 100644 index 0000000..1897533 --- /dev/null +++ b/node_modules/fs-extra/lib/mkdirs/mkdirs.js @@ -0,0 +1,63 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const invalidWin32Path = require('./win32').invalidWin32Path + +const o777 = parseInt('0777', 8) + +function mkdirs (p, opts, callback, made) { + if (typeof opts === 'function') { + callback = opts + opts = {} + } else if (!opts || typeof opts !== 'object') { + opts = { mode: opts } + } + + if (process.platform === 'win32' && invalidWin32Path(p)) { + const errInval = new Error(p + ' contains invalid WIN32 path characters.') + errInval.code = 'EINVAL' + return callback(errInval) + } + + let mode = opts.mode + const xfs = opts.fs || fs + + if (mode === undefined) { + mode = o777 & (~process.umask()) + } + if (!made) made = null + + callback = callback || function () {} + p = path.resolve(p) + + xfs.mkdir(p, mode, er => { + if (!er) { + made = made || p + return callback(null, made) + } + switch (er.code) { + case 'ENOENT': + if (path.dirname(p) === p) return callback(er) + mkdirs(path.dirname(p), opts, (er, made) => { + if (er) callback(er, made) + else mkdirs(p, opts, callback, made) + }) + break + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, (er2, stat) => { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) callback(er, made) + else callback(null, made) + }) + break + } + }) +} + +module.exports = mkdirs diff --git a/node_modules/fs-extra/lib/mkdirs/win32.js b/node_modules/fs-extra/lib/mkdirs/win32.js new file mode 100644 index 0000000..99b3920 --- /dev/null +++ b/node_modules/fs-extra/lib/mkdirs/win32.js @@ -0,0 +1,25 @@ +'use strict' + +const path = require('path') + +// get drive on windows +function getRootPath (p) { + p = path.normalize(path.resolve(p)).split(path.sep) + if (p.length > 0) return p[0] + return null +} + +// http://stackoverflow.com/a/62888/10333 contains more accurate +// TODO: expand to include the rest +const INVALID_PATH_CHARS = /[<>:"|?*]/ + +function invalidWin32Path (p) { + const rp = getRootPath(p) + p = p.replace(rp, '') + return INVALID_PATH_CHARS.test(p) +} + +module.exports = { + getRootPath, + invalidWin32Path +} diff --git a/node_modules/fs-extra/lib/move-sync/index.js b/node_modules/fs-extra/lib/move-sync/index.js new file mode 100644 index 0000000..af90b06 --- /dev/null +++ b/node_modules/fs-extra/lib/move-sync/index.js @@ -0,0 +1,5 @@ +'use strict' + +module.exports = { + moveSync: require('./move-sync') +} diff --git a/node_modules/fs-extra/lib/move-sync/move-sync.js b/node_modules/fs-extra/lib/move-sync/move-sync.js new file mode 100644 index 0000000..20f910c --- /dev/null +++ b/node_modules/fs-extra/lib/move-sync/move-sync.js @@ -0,0 +1,47 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const copySync = require('../copy-sync').copySync +const removeSync = require('../remove').removeSync +const mkdirpSync = require('../mkdirs').mkdirpSync +const stat = require('../util/stat') + +function moveSync (src, dest, opts) { + opts = opts || {} + const overwrite = opts.overwrite || opts.clobber || false + + const { srcStat } = stat.checkPathsSync(src, dest, 'move') + stat.checkParentPathsSync(src, srcStat, dest, 'move') + mkdirpSync(path.dirname(dest)) + return doRename(src, dest, overwrite) +} + +function doRename (src, dest, overwrite) { + if (overwrite) { + removeSync(dest) + return rename(src, dest, overwrite) + } + if (fs.existsSync(dest)) throw new Error('dest already exists.') + return rename(src, dest, overwrite) +} + +function rename (src, dest, overwrite) { + try { + fs.renameSync(src, dest) + } catch (err) { + if (err.code !== 'EXDEV') throw err + return moveAcrossDevice(src, dest, overwrite) + } +} + +function moveAcrossDevice (src, dest, overwrite) { + const opts = { + overwrite, + errorOnExist: true + } + copySync(src, dest, opts) + return removeSync(src) +} + +module.exports = moveSync diff --git a/node_modules/fs-extra/lib/move/index.js b/node_modules/fs-extra/lib/move/index.js new file mode 100644 index 0000000..3785345 --- /dev/null +++ b/node_modules/fs-extra/lib/move/index.js @@ -0,0 +1,6 @@ +'use strict' + +const u = require('universalify').fromCallback +module.exports = { + move: u(require('./move')) +} diff --git a/node_modules/fs-extra/lib/move/move.js b/node_modules/fs-extra/lib/move/move.js new file mode 100644 index 0000000..fa3ea61 --- /dev/null +++ b/node_modules/fs-extra/lib/move/move.js @@ -0,0 +1,65 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const copy = require('../copy').copy +const remove = require('../remove').remove +const mkdirp = require('../mkdirs').mkdirp +const pathExists = require('../path-exists').pathExists +const stat = require('../util/stat') + +function move (src, dest, opts, cb) { + if (typeof opts === 'function') { + cb = opts + opts = {} + } + + const overwrite = opts.overwrite || opts.clobber || false + + stat.checkPaths(src, dest, 'move', (err, stats) => { + if (err) return cb(err) + const { srcStat } = stats + stat.checkParentPaths(src, srcStat, dest, 'move', err => { + if (err) return cb(err) + mkdirp(path.dirname(dest), err => { + if (err) return cb(err) + return doRename(src, dest, overwrite, cb) + }) + }) + }) +} + +function doRename (src, dest, overwrite, cb) { + if (overwrite) { + return remove(dest, err => { + if (err) return cb(err) + return rename(src, dest, overwrite, cb) + }) + } + pathExists(dest, (err, destExists) => { + if (err) return cb(err) + if (destExists) return cb(new Error('dest already exists.')) + return rename(src, dest, overwrite, cb) + }) +} + +function rename (src, dest, overwrite, cb) { + fs.rename(src, dest, err => { + if (!err) return cb() + if (err.code !== 'EXDEV') return cb(err) + return moveAcrossDevice(src, dest, overwrite, cb) + }) +} + +function moveAcrossDevice (src, dest, overwrite, cb) { + const opts = { + overwrite, + errorOnExist: true + } + copy(src, dest, opts, err => { + if (err) return cb(err) + return remove(src, cb) + }) +} + +module.exports = move diff --git a/node_modules/fs-extra/lib/output/index.js b/node_modules/fs-extra/lib/output/index.js new file mode 100644 index 0000000..92297ca --- /dev/null +++ b/node_modules/fs-extra/lib/output/index.js @@ -0,0 +1,40 @@ +'use strict' + +const u = require('universalify').fromCallback +const fs = require('graceful-fs') +const path = require('path') +const mkdir = require('../mkdirs') +const pathExists = require('../path-exists').pathExists + +function outputFile (file, data, encoding, callback) { + if (typeof encoding === 'function') { + callback = encoding + encoding = 'utf8' + } + + const dir = path.dirname(file) + pathExists(dir, (err, itDoes) => { + if (err) return callback(err) + if (itDoes) return fs.writeFile(file, data, encoding, callback) + + mkdir.mkdirs(dir, err => { + if (err) return callback(err) + + fs.writeFile(file, data, encoding, callback) + }) + }) +} + +function outputFileSync (file, ...args) { + const dir = path.dirname(file) + if (fs.existsSync(dir)) { + return fs.writeFileSync(file, ...args) + } + mkdir.mkdirsSync(dir) + fs.writeFileSync(file, ...args) +} + +module.exports = { + outputFile: u(outputFile), + outputFileSync +} diff --git a/node_modules/fs-extra/lib/path-exists/index.js b/node_modules/fs-extra/lib/path-exists/index.js new file mode 100644 index 0000000..ddd9bc7 --- /dev/null +++ b/node_modules/fs-extra/lib/path-exists/index.js @@ -0,0 +1,12 @@ +'use strict' +const u = require('universalify').fromPromise +const fs = require('../fs') + +function pathExists (path) { + return fs.access(path).then(() => true).catch(() => false) +} + +module.exports = { + pathExists: u(pathExists), + pathExistsSync: fs.existsSync +} diff --git a/node_modules/fs-extra/lib/remove/index.js b/node_modules/fs-extra/lib/remove/index.js new file mode 100644 index 0000000..cee5340 --- /dev/null +++ b/node_modules/fs-extra/lib/remove/index.js @@ -0,0 +1,9 @@ +'use strict' + +const u = require('universalify').fromCallback +const rimraf = require('./rimraf') + +module.exports = { + remove: u(rimraf), + removeSync: rimraf.sync +} diff --git a/node_modules/fs-extra/lib/remove/rimraf.js b/node_modules/fs-extra/lib/remove/rimraf.js new file mode 100644 index 0000000..f287e4e --- /dev/null +++ b/node_modules/fs-extra/lib/remove/rimraf.js @@ -0,0 +1,314 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') +const assert = require('assert') + +const isWindows = (process.platform === 'win32') + +function defaults (options) { + const methods = [ + 'unlink', + 'chmod', + 'stat', + 'lstat', + 'rmdir', + 'readdir' + ] + methods.forEach(m => { + options[m] = options[m] || fs[m] + m = m + 'Sync' + options[m] = options[m] || fs[m] + }) + + options.maxBusyTries = options.maxBusyTries || 3 +} + +function rimraf (p, options, cb) { + let busyTries = 0 + + if (typeof options === 'function') { + cb = options + options = {} + } + + assert(p, 'rimraf: missing path') + assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string') + assert.strictEqual(typeof cb, 'function', 'rimraf: callback function required') + assert(options, 'rimraf: invalid options argument provided') + assert.strictEqual(typeof options, 'object', 'rimraf: options should be object') + + defaults(options) + + rimraf_(p, options, function CB (er) { + if (er) { + if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') && + busyTries < options.maxBusyTries) { + busyTries++ + const time = busyTries * 100 + // try again, with the same exact callback as this one. + return setTimeout(() => rimraf_(p, options, CB), time) + } + + // already gone + if (er.code === 'ENOENT') er = null + } + + cb(er) + }) +} + +// Two possible strategies. +// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR +// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR +// +// Both result in an extra syscall when you guess wrong. However, there +// are likely far more normal files in the world than directories. This +// is based on the assumption that a the average number of files per +// directory is >= 1. +// +// If anyone ever complains about this, then I guess the strategy could +// be made configurable somehow. But until then, YAGNI. +function rimraf_ (p, options, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + + // sunos lets the root user unlink directories, which is... weird. + // so we have to lstat here and make sure it's not a dir. + options.lstat(p, (er, st) => { + if (er && er.code === 'ENOENT') { + return cb(null) + } + + // Windows can EPERM on stat. Life is suffering. + if (er && er.code === 'EPERM' && isWindows) { + return fixWinEPERM(p, options, er, cb) + } + + if (st && st.isDirectory()) { + return rmdir(p, options, er, cb) + } + + options.unlink(p, er => { + if (er) { + if (er.code === 'ENOENT') { + return cb(null) + } + if (er.code === 'EPERM') { + return (isWindows) + ? fixWinEPERM(p, options, er, cb) + : rmdir(p, options, er, cb) + } + if (er.code === 'EISDIR') { + return rmdir(p, options, er, cb) + } + } + return cb(er) + }) + }) +} + +function fixWinEPERM (p, options, er, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + if (er) { + assert(er instanceof Error) + } + + options.chmod(p, 0o666, er2 => { + if (er2) { + cb(er2.code === 'ENOENT' ? null : er) + } else { + options.stat(p, (er3, stats) => { + if (er3) { + cb(er3.code === 'ENOENT' ? null : er) + } else if (stats.isDirectory()) { + rmdir(p, options, er, cb) + } else { + options.unlink(p, cb) + } + }) + } + }) +} + +function fixWinEPERMSync (p, options, er) { + let stats + + assert(p) + assert(options) + if (er) { + assert(er instanceof Error) + } + + try { + options.chmodSync(p, 0o666) + } catch (er2) { + if (er2.code === 'ENOENT') { + return + } else { + throw er + } + } + + try { + stats = options.statSync(p) + } catch (er3) { + if (er3.code === 'ENOENT') { + return + } else { + throw er + } + } + + if (stats.isDirectory()) { + rmdirSync(p, options, er) + } else { + options.unlinkSync(p) + } +} + +function rmdir (p, options, originalEr, cb) { + assert(p) + assert(options) + if (originalEr) { + assert(originalEr instanceof Error) + } + assert(typeof cb === 'function') + + // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) + // if we guessed wrong, and it's not a directory, then + // raise the original error. + options.rmdir(p, er => { + if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) { + rmkids(p, options, cb) + } else if (er && er.code === 'ENOTDIR') { + cb(originalEr) + } else { + cb(er) + } + }) +} + +function rmkids (p, options, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + + options.readdir(p, (er, files) => { + if (er) return cb(er) + + let n = files.length + let errState + + if (n === 0) return options.rmdir(p, cb) + + files.forEach(f => { + rimraf(path.join(p, f), options, er => { + if (errState) { + return + } + if (er) return cb(errState = er) + if (--n === 0) { + options.rmdir(p, cb) + } + }) + }) + }) +} + +// this looks simpler, and is strictly *faster*, but will +// tie up the JavaScript thread and fail on excessively +// deep directory trees. +function rimrafSync (p, options) { + let st + + options = options || {} + defaults(options) + + assert(p, 'rimraf: missing path') + assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string') + assert(options, 'rimraf: missing options') + assert.strictEqual(typeof options, 'object', 'rimraf: options should be object') + + try { + st = options.lstatSync(p) + } catch (er) { + if (er.code === 'ENOENT') { + return + } + + // Windows can EPERM on stat. Life is suffering. + if (er.code === 'EPERM' && isWindows) { + fixWinEPERMSync(p, options, er) + } + } + + try { + // sunos lets the root user unlink directories, which is... weird. + if (st && st.isDirectory()) { + rmdirSync(p, options, null) + } else { + options.unlinkSync(p) + } + } catch (er) { + if (er.code === 'ENOENT') { + return + } else if (er.code === 'EPERM') { + return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) + } else if (er.code !== 'EISDIR') { + throw er + } + rmdirSync(p, options, er) + } +} + +function rmdirSync (p, options, originalEr) { + assert(p) + assert(options) + if (originalEr) { + assert(originalEr instanceof Error) + } + + try { + options.rmdirSync(p) + } catch (er) { + if (er.code === 'ENOTDIR') { + throw originalEr + } else if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') { + rmkidsSync(p, options) + } else if (er.code !== 'ENOENT') { + throw er + } + } +} + +function rmkidsSync (p, options) { + assert(p) + assert(options) + options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options)) + + if (isWindows) { + // We only end up here once we got ENOTEMPTY at least once, and + // at this point, we are guaranteed to have removed all the kids. + // So, we know that it won't be ENOENT or ENOTDIR or anything else. + // try really hard to delete stuff on windows, because it has a + // PROFOUNDLY annoying habit of not closing handles promptly when + // files are deleted, resulting in spurious ENOTEMPTY errors. + const startTime = Date.now() + do { + try { + const ret = options.rmdirSync(p, options) + return ret + } catch (er) { } + } while (Date.now() - startTime < 500) // give up after 500ms + } else { + const ret = options.rmdirSync(p, options) + return ret + } +} + +module.exports = rimraf +rimraf.sync = rimrafSync diff --git a/node_modules/fs-extra/lib/util/buffer.js b/node_modules/fs-extra/lib/util/buffer.js new file mode 100644 index 0000000..dabf288 --- /dev/null +++ b/node_modules/fs-extra/lib/util/buffer.js @@ -0,0 +1,12 @@ +'use strict' +/* eslint-disable node/no-deprecated-api */ +module.exports = function (size) { + if (typeof Buffer.allocUnsafe === 'function') { + try { + return Buffer.allocUnsafe(size) + } catch (e) { + return new Buffer(size) + } + } + return new Buffer(size) +} diff --git a/node_modules/fs-extra/lib/util/stat.js b/node_modules/fs-extra/lib/util/stat.js new file mode 100644 index 0000000..350cb9f --- /dev/null +++ b/node_modules/fs-extra/lib/util/stat.js @@ -0,0 +1,172 @@ +'use strict' + +const fs = require('graceful-fs') +const path = require('path') + +const NODE_VERSION_MAJOR_WITH_BIGINT = 10 +const NODE_VERSION_MINOR_WITH_BIGINT = 5 +const NODE_VERSION_PATCH_WITH_BIGINT = 0 +const nodeVersion = process.versions.node.split('.') +const nodeVersionMajor = Number.parseInt(nodeVersion[0], 10) +const nodeVersionMinor = Number.parseInt(nodeVersion[1], 10) +const nodeVersionPatch = Number.parseInt(nodeVersion[2], 10) + +function nodeSupportsBigInt () { + if (nodeVersionMajor > NODE_VERSION_MAJOR_WITH_BIGINT) { + return true + } else if (nodeVersionMajor === NODE_VERSION_MAJOR_WITH_BIGINT) { + if (nodeVersionMinor > NODE_VERSION_MINOR_WITH_BIGINT) { + return true + } else if (nodeVersionMinor === NODE_VERSION_MINOR_WITH_BIGINT) { + if (nodeVersionPatch >= NODE_VERSION_PATCH_WITH_BIGINT) { + return true + } + } + } + return false +} + +function getStats (src, dest, cb) { + if (nodeSupportsBigInt()) { + fs.stat(src, { bigint: true }, (err, srcStat) => { + if (err) return cb(err) + fs.stat(dest, { bigint: true }, (err, destStat) => { + if (err) { + if (err.code === 'ENOENT') return cb(null, { srcStat, destStat: null }) + return cb(err) + } + return cb(null, { srcStat, destStat }) + }) + }) + } else { + fs.stat(src, (err, srcStat) => { + if (err) return cb(err) + fs.stat(dest, (err, destStat) => { + if (err) { + if (err.code === 'ENOENT') return cb(null, { srcStat, destStat: null }) + return cb(err) + } + return cb(null, { srcStat, destStat }) + }) + }) + } +} + +function getStatsSync (src, dest) { + let srcStat, destStat + if (nodeSupportsBigInt()) { + srcStat = fs.statSync(src, { bigint: true }) + } else { + srcStat = fs.statSync(src) + } + try { + if (nodeSupportsBigInt()) { + destStat = fs.statSync(dest, { bigint: true }) + } else { + destStat = fs.statSync(dest) + } + } catch (err) { + if (err.code === 'ENOENT') return { srcStat, destStat: null } + throw err + } + return { srcStat, destStat } +} + +function checkPaths (src, dest, funcName, cb) { + getStats(src, dest, (err, stats) => { + if (err) return cb(err) + const { srcStat, destStat } = stats + if (destStat && destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) { + return cb(new Error('Source and destination must not be the same.')) + } + if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { + return cb(new Error(errMsg(src, dest, funcName))) + } + return cb(null, { srcStat, destStat }) + }) +} + +function checkPathsSync (src, dest, funcName) { + const { srcStat, destStat } = getStatsSync(src, dest) + if (destStat && destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) { + throw new Error('Source and destination must not be the same.') + } + if (srcStat.isDirectory() && isSrcSubdir(src, dest)) { + throw new Error(errMsg(src, dest, funcName)) + } + return { srcStat, destStat } +} + +// recursively check if dest parent is a subdirectory of src. +// It works for all file types including symlinks since it +// checks the src and dest inodes. It starts from the deepest +// parent and stops once it reaches the src parent or the root path. +function checkParentPaths (src, srcStat, dest, funcName, cb) { + const srcParent = path.resolve(path.dirname(src)) + const destParent = path.resolve(path.dirname(dest)) + if (destParent === srcParent || destParent === path.parse(destParent).root) return cb() + if (nodeSupportsBigInt()) { + fs.stat(destParent, { bigint: true }, (err, destStat) => { + if (err) { + if (err.code === 'ENOENT') return cb() + return cb(err) + } + if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) { + return cb(new Error(errMsg(src, dest, funcName))) + } + return checkParentPaths(src, srcStat, destParent, funcName, cb) + }) + } else { + fs.stat(destParent, (err, destStat) => { + if (err) { + if (err.code === 'ENOENT') return cb() + return cb(err) + } + if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) { + return cb(new Error(errMsg(src, dest, funcName))) + } + return checkParentPaths(src, srcStat, destParent, funcName, cb) + }) + } +} + +function checkParentPathsSync (src, srcStat, dest, funcName) { + const srcParent = path.resolve(path.dirname(src)) + const destParent = path.resolve(path.dirname(dest)) + if (destParent === srcParent || destParent === path.parse(destParent).root) return + let destStat + try { + if (nodeSupportsBigInt()) { + destStat = fs.statSync(destParent, { bigint: true }) + } else { + destStat = fs.statSync(destParent) + } + } catch (err) { + if (err.code === 'ENOENT') return + throw err + } + if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) { + throw new Error(errMsg(src, dest, funcName)) + } + return checkParentPathsSync(src, srcStat, destParent, funcName) +} + +// return true if dest is a subdir of src, otherwise false. +// It only checks the path strings. +function isSrcSubdir (src, dest) { + const srcArr = path.resolve(src).split(path.sep).filter(i => i) + const destArr = path.resolve(dest).split(path.sep).filter(i => i) + return srcArr.reduce((acc, cur, i) => acc && destArr[i] === cur, true) +} + +function errMsg (src, dest, funcName) { + return `Cannot ${funcName} '${src}' to a subdirectory of itself, '${dest}'.` +} + +module.exports = { + checkPaths, + checkPathsSync, + checkParentPaths, + checkParentPathsSync, + isSrcSubdir +} diff --git a/node_modules/fs-extra/lib/util/utimes.js b/node_modules/fs-extra/lib/util/utimes.js new file mode 100644 index 0000000..8916a1b --- /dev/null +++ b/node_modules/fs-extra/lib/util/utimes.js @@ -0,0 +1,79 @@ +'use strict' + +const fs = require('graceful-fs') +const os = require('os') +const path = require('path') + +// HFS, ext{2,3}, FAT do not, Node.js v0.10 does not +function hasMillisResSync () { + let tmpfile = path.join('millis-test-sync' + Date.now().toString() + Math.random().toString().slice(2)) + tmpfile = path.join(os.tmpdir(), tmpfile) + + // 550 millis past UNIX epoch + const d = new Date(1435410243862) + fs.writeFileSync(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141') + const fd = fs.openSync(tmpfile, 'r+') + fs.futimesSync(fd, d, d) + fs.closeSync(fd) + return fs.statSync(tmpfile).mtime > 1435410243000 +} + +function hasMillisRes (callback) { + let tmpfile = path.join('millis-test' + Date.now().toString() + Math.random().toString().slice(2)) + tmpfile = path.join(os.tmpdir(), tmpfile) + + // 550 millis past UNIX epoch + const d = new Date(1435410243862) + fs.writeFile(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141', err => { + if (err) return callback(err) + fs.open(tmpfile, 'r+', (err, fd) => { + if (err) return callback(err) + fs.futimes(fd, d, d, err => { + if (err) return callback(err) + fs.close(fd, err => { + if (err) return callback(err) + fs.stat(tmpfile, (err, stats) => { + if (err) return callback(err) + callback(null, stats.mtime > 1435410243000) + }) + }) + }) + }) + }) +} + +function timeRemoveMillis (timestamp) { + if (typeof timestamp === 'number') { + return Math.floor(timestamp / 1000) * 1000 + } else if (timestamp instanceof Date) { + return new Date(Math.floor(timestamp.getTime() / 1000) * 1000) + } else { + throw new Error('fs-extra: timeRemoveMillis() unknown parameter type') + } +} + +function utimesMillis (path, atime, mtime, callback) { + // if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback) + fs.open(path, 'r+', (err, fd) => { + if (err) return callback(err) + fs.futimes(fd, atime, mtime, futimesErr => { + fs.close(fd, closeErr => { + if (callback) callback(futimesErr || closeErr) + }) + }) + }) +} + +function utimesMillisSync (path, atime, mtime) { + const fd = fs.openSync(path, 'r+') + fs.futimesSync(fd, atime, mtime) + return fs.closeSync(fd) +} + +module.exports = { + hasMillisRes, + hasMillisResSync, + timeRemoveMillis, + utimesMillis, + utimesMillisSync +} diff --git a/node_modules/fs-extra/package.json b/node_modules/fs-extra/package.json new file mode 100644 index 0000000..559d88b --- /dev/null +++ b/node_modules/fs-extra/package.json @@ -0,0 +1,69 @@ +{ + "name": "fs-extra", + "version": "8.1.0", + "description": "fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf.", + "engines": { + "node": ">=6 <7 || >=8" + }, + "homepage": "https://github.com/jprichardson/node-fs-extra", + "repository": { + "type": "git", + "url": "https://github.com/jprichardson/node-fs-extra" + }, + "keywords": [ + "fs", + "file", + "file system", + "copy", + "directory", + "extra", + "mkdirp", + "mkdir", + "mkdirs", + "recursive", + "json", + "read", + "write", + "extra", + "delete", + "remove", + "touch", + "create", + "text", + "output", + "move" + ], + "author": "JP Richardson ", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "devDependencies": { + "coveralls": "^3.0.0", + "istanbul": "^0.4.5", + "klaw": "^2.1.1", + "klaw-sync": "^3.0.2", + "minimist": "^1.1.1", + "mocha": "^5.0.5", + "proxyquire": "^2.0.1", + "read-dir-files": "^0.1.1", + "semver": "^5.3.0", + "standard": "^12.0.1" + }, + "main": "./lib/index.js", + "files": [ + "lib/", + "!lib/**/__tests__/" + ], + "scripts": { + "full-ci": "npm run lint && npm run coverage", + "coverage": "istanbul cover -i 'lib/**' -x '**/__tests__/**' test.js", + "coveralls": "coveralls < coverage/lcov.info", + "lint": "standard", + "test-find": "find ./lib/**/__tests__ -name *.test.js | xargs mocha", + "test": "npm run lint && npm run unit", + "unit": "node test.js" + } +} diff --git a/node_modules/fs.realpath/LICENSE b/node_modules/fs.realpath/LICENSE new file mode 100644 index 0000000..5bd884c --- /dev/null +++ b/node_modules/fs.realpath/LICENSE @@ -0,0 +1,43 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---- + +This library bundles a version of the `fs.realpath` and `fs.realpathSync` +methods from Node.js v0.10 under the terms of the Node.js MIT license. + +Node's license follows, also included at the header of `old.js` which contains +the licensed code: + + Copyright Joyent, Inc. and other Node contributors. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. diff --git a/node_modules/fs.realpath/README.md b/node_modules/fs.realpath/README.md new file mode 100644 index 0000000..a42ceac --- /dev/null +++ b/node_modules/fs.realpath/README.md @@ -0,0 +1,33 @@ +# fs.realpath + +A backwards-compatible fs.realpath for Node v6 and above + +In Node v6, the JavaScript implementation of fs.realpath was replaced +with a faster (but less resilient) native implementation. That raises +new and platform-specific errors and cannot handle long or excessively +symlink-looping paths. + +This module handles those cases by detecting the new errors and +falling back to the JavaScript implementation. On versions of Node +prior to v6, it has no effect. + +## USAGE + +```js +var rp = require('fs.realpath') + +// async version +rp.realpath(someLongAndLoopingPath, function (er, real) { + // the ELOOP was handled, but it was a bit slower +}) + +// sync version +var real = rp.realpathSync(someLongAndLoopingPath) + +// monkeypatch at your own risk! +// This replaces the fs.realpath/fs.realpathSync builtins +rp.monkeypatch() + +// un-do the monkeypatching +rp.unmonkeypatch() +``` diff --git a/node_modules/fs.realpath/index.js b/node_modules/fs.realpath/index.js new file mode 100644 index 0000000..b09c7c7 --- /dev/null +++ b/node_modules/fs.realpath/index.js @@ -0,0 +1,66 @@ +module.exports = realpath +realpath.realpath = realpath +realpath.sync = realpathSync +realpath.realpathSync = realpathSync +realpath.monkeypatch = monkeypatch +realpath.unmonkeypatch = unmonkeypatch + +var fs = require('fs') +var origRealpath = fs.realpath +var origRealpathSync = fs.realpathSync + +var version = process.version +var ok = /^v[0-5]\./.test(version) +var old = require('./old.js') + +function newError (er) { + return er && er.syscall === 'realpath' && ( + er.code === 'ELOOP' || + er.code === 'ENOMEM' || + er.code === 'ENAMETOOLONG' + ) +} + +function realpath (p, cache, cb) { + if (ok) { + return origRealpath(p, cache, cb) + } + + if (typeof cache === 'function') { + cb = cache + cache = null + } + origRealpath(p, cache, function (er, result) { + if (newError(er)) { + old.realpath(p, cache, cb) + } else { + cb(er, result) + } + }) +} + +function realpathSync (p, cache) { + if (ok) { + return origRealpathSync(p, cache) + } + + try { + return origRealpathSync(p, cache) + } catch (er) { + if (newError(er)) { + return old.realpathSync(p, cache) + } else { + throw er + } + } +} + +function monkeypatch () { + fs.realpath = realpath + fs.realpathSync = realpathSync +} + +function unmonkeypatch () { + fs.realpath = origRealpath + fs.realpathSync = origRealpathSync +} diff --git a/node_modules/fs.realpath/old.js b/node_modules/fs.realpath/old.js new file mode 100644 index 0000000..b40305e --- /dev/null +++ b/node_modules/fs.realpath/old.js @@ -0,0 +1,303 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var pathModule = require('path'); +var isWindows = process.platform === 'win32'; +var fs = require('fs'); + +// JavaScript implementation of realpath, ported from node pre-v6 + +var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); + +function rethrow() { + // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and + // is fairly slow to generate. + var callback; + if (DEBUG) { + var backtrace = new Error; + callback = debugCallback; + } else + callback = missingCallback; + + return callback; + + function debugCallback(err) { + if (err) { + backtrace.message = err.message; + err = backtrace; + missingCallback(err); + } + } + + function missingCallback(err) { + if (err) { + if (process.throwDeprecation) + throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs + else if (!process.noDeprecation) { + var msg = 'fs: missing callback ' + (err.stack || err.message); + if (process.traceDeprecation) + console.trace(msg); + else + console.error(msg); + } + } + } +} + +function maybeCallback(cb) { + return typeof cb === 'function' ? cb : rethrow(); +} + +var normalize = pathModule.normalize; + +// Regexp that finds the next partion of a (partial) path +// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] +if (isWindows) { + var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; +} else { + var nextPartRe = /(.*?)(?:[\/]+|$)/g; +} + +// Regex to find the device root, including trailing slash. E.g. 'c:\\'. +if (isWindows) { + var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; +} else { + var splitRootRe = /^[\/]*/; +} + +exports.realpathSync = function realpathSync(p, cache) { + // make p is absolute + p = pathModule.resolve(p); + + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return cache[p]; + } + + var original = p, + seenLinks = {}, + knownHard = {}; + + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; + + start(); + + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstatSync(base); + knownHard[base] = true; + } + } + + // walk down the path, swapping out linked pathparts for their real + // values + // NB: p.length changes. + while (pos < p.length) { + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; + + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + continue; + } + + var resolvedLink; + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // some known symbolic link. no need to stat again. + resolvedLink = cache[base]; + } else { + var stat = fs.lstatSync(base); + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + continue; + } + + // read the link if it wasn't read before + // dev/ino always return 0 on windows, so skip the check. + var linkTarget = null; + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + linkTarget = seenLinks[id]; + } + } + if (linkTarget === null) { + fs.statSync(base); + linkTarget = fs.readlinkSync(base); + } + resolvedLink = pathModule.resolve(previous, linkTarget); + // track this, if given a cache. + if (cache) cache[base] = resolvedLink; + if (!isWindows) seenLinks[id] = linkTarget; + } + + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } + + if (cache) cache[original] = p; + + return p; +}; + + +exports.realpath = function realpath(p, cache, cb) { + if (typeof cb !== 'function') { + cb = maybeCallback(cache); + cache = null; + } + + // make p is absolute + p = pathModule.resolve(p); + + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return process.nextTick(cb.bind(null, null, cache[p])); + } + + var original = p, + seenLinks = {}, + knownHard = {}; + + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; + + start(); + + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstat(base, function(err) { + if (err) return cb(err); + knownHard[base] = true; + LOOP(); + }); + } else { + process.nextTick(LOOP); + } + } + + // walk down the path, swapping out linked pathparts for their real + // values + function LOOP() { + // stop if scanned past end of path + if (pos >= p.length) { + if (cache) cache[original] = p; + return cb(null, p); + } + + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; + + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + return process.nextTick(LOOP); + } + + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // known symbolic link. no need to stat again. + return gotResolvedLink(cache[base]); + } + + return fs.lstat(base, gotStat); + } + + function gotStat(err, stat) { + if (err) return cb(err); + + // if not a symlink, skip to the next path part + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + return process.nextTick(LOOP); + } + + // stat & read the link if not read before + // call gotTarget as soon as the link target is known + // dev/ino always return 0 on windows, so skip the check. + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + return gotTarget(null, seenLinks[id], base); + } + } + fs.stat(base, function(err) { + if (err) return cb(err); + + fs.readlink(base, function(err, target) { + if (!isWindows) seenLinks[id] = target; + gotTarget(err, target); + }); + }); + } + + function gotTarget(err, target, base) { + if (err) return cb(err); + + var resolvedLink = pathModule.resolve(previous, target); + if (cache) cache[base] = resolvedLink; + gotResolvedLink(resolvedLink); + } + + function gotResolvedLink(resolvedLink) { + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } +}; diff --git a/node_modules/fs.realpath/package.json b/node_modules/fs.realpath/package.json new file mode 100644 index 0000000..3edc57d --- /dev/null +++ b/node_modules/fs.realpath/package.json @@ -0,0 +1,26 @@ +{ + "name": "fs.realpath", + "version": "1.0.0", + "description": "Use node's fs.realpath, but fall back to the JS implementation if the native one fails", + "main": "index.js", + "dependencies": {}, + "devDependencies": {}, + "scripts": { + "test": "tap test/*.js --cov" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/isaacs/fs.realpath.git" + }, + "keywords": [ + "realpath", + "fs", + "polyfill" + ], + "author": "Isaac Z. Schlueter (http://blog.izs.me/)", + "license": "ISC", + "files": [ + "old.js", + "index.js" + ] +} diff --git a/node_modules/glob/LICENSE b/node_modules/glob/LICENSE new file mode 100644 index 0000000..42ca266 --- /dev/null +++ b/node_modules/glob/LICENSE @@ -0,0 +1,21 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +## Glob Logo + +Glob's logo created by Tanya Brassie , licensed +under a Creative Commons Attribution-ShareAlike 4.0 International License +https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/node_modules/glob/README.md b/node_modules/glob/README.md new file mode 100644 index 0000000..83f0c83 --- /dev/null +++ b/node_modules/glob/README.md @@ -0,0 +1,378 @@ +# Glob + +Match files using the patterns the shell uses, like stars and stuff. + +[![Build Status](https://travis-ci.org/isaacs/node-glob.svg?branch=master)](https://travis-ci.org/isaacs/node-glob/) [![Build Status](https://ci.appveyor.com/api/projects/status/kd7f3yftf7unxlsx?svg=true)](https://ci.appveyor.com/project/isaacs/node-glob) [![Coverage Status](https://coveralls.io/repos/isaacs/node-glob/badge.svg?branch=master&service=github)](https://coveralls.io/github/isaacs/node-glob?branch=master) + +This is a glob implementation in JavaScript. It uses the `minimatch` +library to do its matching. + +![a fun cartoon logo made of glob characters](logo/glob.png) + +## Usage + +Install with npm + +``` +npm i glob +``` + +```javascript +var glob = require("glob") + +// options is optional +glob("**/*.js", options, function (er, files) { + // files is an array of filenames. + // If the `nonull` option is set, and nothing + // was found, then files is ["**/*.js"] + // er is an error object or null. +}) +``` + +## Glob Primer + +"Globs" are the patterns you type when you do stuff like `ls *.js` on +the command line, or put `build/*` in a `.gitignore` file. + +Before parsing the path part patterns, braced sections are expanded +into a set. Braced sections start with `{` and end with `}`, with any +number of comma-delimited sections within. Braced sections may contain +slash characters, so `a{/b/c,bcd}` would expand into `a/b/c` and `abcd`. + +The following characters have special magic meaning when used in a +path portion: + +* `*` Matches 0 or more characters in a single path portion +* `?` Matches 1 character +* `[...]` Matches a range of characters, similar to a RegExp range. + If the first character of the range is `!` or `^` then it matches + any character not in the range. +* `!(pattern|pattern|pattern)` Matches anything that does not match + any of the patterns provided. +* `?(pattern|pattern|pattern)` Matches zero or one occurrence of the + patterns provided. +* `+(pattern|pattern|pattern)` Matches one or more occurrences of the + patterns provided. +* `*(a|b|c)` Matches zero or more occurrences of the patterns provided +* `@(pattern|pat*|pat?erN)` Matches exactly one of the patterns + provided +* `**` If a "globstar" is alone in a path portion, then it matches + zero or more directories and subdirectories searching for matches. + It does not crawl symlinked directories. + +### Dots + +If a file or directory path portion has a `.` as the first character, +then it will not match any glob pattern unless that pattern's +corresponding path part also has a `.` as its first character. + +For example, the pattern `a/.*/c` would match the file at `a/.b/c`. +However the pattern `a/*/c` would not, because `*` does not start with +a dot character. + +You can make glob treat dots as normal characters by setting +`dot:true` in the options. + +### Basename Matching + +If you set `matchBase:true` in the options, and the pattern has no +slashes in it, then it will seek for any file anywhere in the tree +with a matching basename. For example, `*.js` would match +`test/simple/basic.js`. + +### Empty Sets + +If no matching files are found, then an empty array is returned. This +differs from the shell, where the pattern itself is returned. For +example: + + $ echo a*s*d*f + a*s*d*f + +To get the bash-style behavior, set the `nonull:true` in the options. + +### See Also: + +* `man sh` +* `man bash` (Search for "Pattern Matching") +* `man 3 fnmatch` +* `man 5 gitignore` +* [minimatch documentation](https://github.com/isaacs/minimatch) + +## glob.hasMagic(pattern, [options]) + +Returns `true` if there are any special characters in the pattern, and +`false` otherwise. + +Note that the options affect the results. If `noext:true` is set in +the options object, then `+(a|b)` will not be considered a magic +pattern. If the pattern has a brace expansion, like `a/{b/c,x/y}` +then that is considered magical, unless `nobrace:true` is set in the +options. + +## glob(pattern, [options], cb) + +* `pattern` `{String}` Pattern to be matched +* `options` `{Object}` +* `cb` `{Function}` + * `err` `{Error | null}` + * `matches` `{Array}` filenames found matching the pattern + +Perform an asynchronous glob search. + +## glob.sync(pattern, [options]) + +* `pattern` `{String}` Pattern to be matched +* `options` `{Object}` +* return: `{Array}` filenames found matching the pattern + +Perform a synchronous glob search. + +## Class: glob.Glob + +Create a Glob object by instantiating the `glob.Glob` class. + +```javascript +var Glob = require("glob").Glob +var mg = new Glob(pattern, options, cb) +``` + +It's an EventEmitter, and starts walking the filesystem to find matches +immediately. + +### new glob.Glob(pattern, [options], [cb]) + +* `pattern` `{String}` pattern to search for +* `options` `{Object}` +* `cb` `{Function}` Called when an error occurs, or matches are found + * `err` `{Error | null}` + * `matches` `{Array}` filenames found matching the pattern + +Note that if the `sync` flag is set in the options, then matches will +be immediately available on the `g.found` member. + +### Properties + +* `minimatch` The minimatch object that the glob uses. +* `options` The options object passed in. +* `aborted` Boolean which is set to true when calling `abort()`. There + is no way at this time to continue a glob search after aborting, but + you can re-use the statCache to avoid having to duplicate syscalls. +* `cache` Convenience object. Each field has the following possible + values: + * `false` - Path does not exist + * `true` - Path exists + * `'FILE'` - Path exists, and is not a directory + * `'DIR'` - Path exists, and is a directory + * `[file, entries, ...]` - Path exists, is a directory, and the + array value is the results of `fs.readdir` +* `statCache` Cache of `fs.stat` results, to prevent statting the same + path multiple times. +* `symlinks` A record of which paths are symbolic links, which is + relevant in resolving `**` patterns. +* `realpathCache` An optional object which is passed to `fs.realpath` + to minimize unnecessary syscalls. It is stored on the instantiated + Glob object, and may be re-used. + +### Events + +* `end` When the matching is finished, this is emitted with all the + matches found. If the `nonull` option is set, and no match was found, + then the `matches` list contains the original pattern. The matches + are sorted, unless the `nosort` flag is set. +* `match` Every time a match is found, this is emitted with the specific + thing that matched. It is not deduplicated or resolved to a realpath. +* `error` Emitted when an unexpected error is encountered, or whenever + any fs error occurs if `options.strict` is set. +* `abort` When `abort()` is called, this event is raised. + +### Methods + +* `pause` Temporarily stop the search +* `resume` Resume the search +* `abort` Stop the search forever + +### Options + +All the options that can be passed to Minimatch can also be passed to +Glob to change pattern matching behavior. Also, some have been added, +or have glob-specific ramifications. + +All options are false by default, unless otherwise noted. + +All options are added to the Glob object, as well. + +If you are running many `glob` operations, you can pass a Glob object +as the `options` argument to a subsequent operation to shortcut some +`stat` and `readdir` calls. At the very least, you may pass in shared +`symlinks`, `statCache`, `realpathCache`, and `cache` options, so that +parallel glob operations will be sped up by sharing information about +the filesystem. + +* `cwd` The current working directory in which to search. Defaults + to `process.cwd()`. +* `root` The place where patterns starting with `/` will be mounted + onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix + systems, and `C:\` or some such on Windows.) +* `dot` Include `.dot` files in normal matches and `globstar` matches. + Note that an explicit dot in a portion of the pattern will always + match dot files. +* `nomount` By default, a pattern starting with a forward-slash will be + "mounted" onto the root setting, so that a valid filesystem path is + returned. Set this flag to disable that behavior. +* `mark` Add a `/` character to directory matches. Note that this + requires additional stat calls. +* `nosort` Don't sort the results. +* `stat` Set to true to stat *all* results. This reduces performance + somewhat, and is completely unnecessary, unless `readdir` is presumed + to be an untrustworthy indicator of file existence. +* `silent` When an unusual error is encountered when attempting to + read a directory, a warning will be printed to stderr. Set the + `silent` option to true to suppress these warnings. +* `strict` When an unusual error is encountered when attempting to + read a directory, the process will just continue on in search of + other matches. Set the `strict` option to raise an error in these + cases. +* `cache` See `cache` property above. Pass in a previously generated + cache object to save some fs calls. +* `statCache` A cache of results of filesystem information, to prevent + unnecessary stat calls. While it should not normally be necessary + to set this, you may pass the statCache from one glob() call to the + options object of another, if you know that the filesystem will not + change between calls. (See "Race Conditions" below.) +* `symlinks` A cache of known symbolic links. You may pass in a + previously generated `symlinks` object to save `lstat` calls when + resolving `**` matches. +* `sync` DEPRECATED: use `glob.sync(pattern, opts)` instead. +* `nounique` In some cases, brace-expanded patterns can result in the + same file showing up multiple times in the result set. By default, + this implementation prevents duplicates in the result set. Set this + flag to disable that behavior. +* `nonull` Set to never return an empty set, instead returning a set + containing the pattern itself. This is the default in glob(3). +* `debug` Set to enable debug logging in minimatch and glob. +* `nobrace` Do not expand `{a,b}` and `{1..3}` brace sets. +* `noglobstar` Do not match `**` against multiple filenames. (Ie, + treat it as a normal `*` instead.) +* `noext` Do not match `+(a|b)` "extglob" patterns. +* `nocase` Perform a case-insensitive match. Note: on + case-insensitive filesystems, non-magic patterns will match by + default, since `stat` and `readdir` will not raise errors. +* `matchBase` Perform a basename-only match if the pattern does not + contain any slash characters. That is, `*.js` would be treated as + equivalent to `**/*.js`, matching all js files in all directories. +* `nodir` Do not match directories, only files. (Note: to match + *only* directories, simply put a `/` at the end of the pattern.) +* `ignore` Add a pattern or an array of glob patterns to exclude matches. + Note: `ignore` patterns are *always* in `dot:true` mode, regardless + of any other settings. +* `follow` Follow symlinked directories when expanding `**` patterns. + Note that this can result in a lot of duplicate references in the + presence of cyclic links. +* `realpath` Set to true to call `fs.realpath` on all of the results. + In the case of a symlink that cannot be resolved, the full absolute + path to the matched entry is returned (though it will usually be a + broken symlink) +* `absolute` Set to true to always receive absolute paths for matched + files. Unlike `realpath`, this also affects the values returned in + the `match` event. +* `fs` File-system object with Node's `fs` API. By default, the built-in + `fs` module will be used. Set to a volume provided by a library like + `memfs` to avoid using the "real" file-system. + +## Comparisons to other fnmatch/glob implementations + +While strict compliance with the existing standards is a worthwhile +goal, some discrepancies exist between node-glob and other +implementations, and are intentional. + +The double-star character `**` is supported by default, unless the +`noglobstar` flag is set. This is supported in the manner of bsdglob +and bash 4.3, where `**` only has special significance if it is the only +thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but +`a/**b` will not. + +Note that symlinked directories are not crawled as part of a `**`, +though their contents may match against subsequent portions of the +pattern. This prevents infinite loops and duplicates and the like. + +If an escaped pattern has no matches, and the `nonull` flag is set, +then glob returns the pattern as-provided, rather than +interpreting the character escapes. For example, +`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than +`"*a?"`. This is akin to setting the `nullglob` option in bash, except +that it does not resolve escaped pattern characters. + +If brace expansion is not disabled, then it is performed before any +other interpretation of the glob pattern. Thus, a pattern like +`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded +**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are +checked for validity. Since those two are valid, matching proceeds. + +### Comments and Negation + +Previously, this module let you mark a pattern as a "comment" if it +started with a `#` character, or a "negated" pattern if it started +with a `!` character. + +These options were deprecated in version 5, and removed in version 6. + +To specify things that should not match, use the `ignore` option. + +## Windows + +**Please only use forward-slashes in glob expressions.** + +Though windows uses either `/` or `\` as its path separator, only `/` +characters are used by this glob implementation. You must use +forward-slashes **only** in glob expressions. Back-slashes will always +be interpreted as escape characters, not path separators. + +Results from absolute patterns such as `/foo/*` are mounted onto the +root setting using `path.join`. On windows, this will by default result +in `/foo/*` matching `C:\foo\bar.txt`. + +## Race Conditions + +Glob searching, by its very nature, is susceptible to race conditions, +since it relies on directory walking and such. + +As a result, it is possible that a file that exists when glob looks for +it may have been deleted or modified by the time it returns the result. + +As part of its internal implementation, this program caches all stat +and readdir calls that it makes, in order to cut down on system +overhead. However, this also makes it even more susceptible to races, +especially if the cache or statCache objects are reused between glob +calls. + +Users are thus advised not to use a glob result as a guarantee of +filesystem state in the face of rapid changes. For the vast majority +of operations, this is never a problem. + +## Glob Logo +Glob's logo was created by [Tanya Brassie](http://tanyabrassie.com/). Logo files can be found [here](https://github.com/isaacs/node-glob/tree/master/logo). + +The logo is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/). + +## Contributing + +Any change to behavior (including bugfixes) must come with a test. + +Patches that fail tests or reduce performance will be rejected. + +``` +# to run tests +npm test + +# to re-generate test fixtures +npm run test-regen + +# to benchmark against bash/zsh +npm run bench + +# to profile javascript +npm run prof +``` + +![](oh-my-glob.gif) diff --git a/node_modules/glob/common.js b/node_modules/glob/common.js new file mode 100644 index 0000000..424c46e --- /dev/null +++ b/node_modules/glob/common.js @@ -0,0 +1,238 @@ +exports.setopts = setopts +exports.ownProp = ownProp +exports.makeAbs = makeAbs +exports.finish = finish +exports.mark = mark +exports.isIgnored = isIgnored +exports.childrenIgnored = childrenIgnored + +function ownProp (obj, field) { + return Object.prototype.hasOwnProperty.call(obj, field) +} + +var fs = require("fs") +var path = require("path") +var minimatch = require("minimatch") +var isAbsolute = require("path-is-absolute") +var Minimatch = minimatch.Minimatch + +function alphasort (a, b) { + return a.localeCompare(b, 'en') +} + +function setupIgnores (self, options) { + self.ignore = options.ignore || [] + + if (!Array.isArray(self.ignore)) + self.ignore = [self.ignore] + + if (self.ignore.length) { + self.ignore = self.ignore.map(ignoreMap) + } +} + +// ignore patterns are always in dot:true mode. +function ignoreMap (pattern) { + var gmatcher = null + if (pattern.slice(-3) === '/**') { + var gpattern = pattern.replace(/(\/\*\*)+$/, '') + gmatcher = new Minimatch(gpattern, { dot: true }) + } + + return { + matcher: new Minimatch(pattern, { dot: true }), + gmatcher: gmatcher + } +} + +function setopts (self, pattern, options) { + if (!options) + options = {} + + // base-matching: just use globstar for that. + if (options.matchBase && -1 === pattern.indexOf("/")) { + if (options.noglobstar) { + throw new Error("base matching requires globstar") + } + pattern = "**/" + pattern + } + + self.silent = !!options.silent + self.pattern = pattern + self.strict = options.strict !== false + self.realpath = !!options.realpath + self.realpathCache = options.realpathCache || Object.create(null) + self.follow = !!options.follow + self.dot = !!options.dot + self.mark = !!options.mark + self.nodir = !!options.nodir + if (self.nodir) + self.mark = true + self.sync = !!options.sync + self.nounique = !!options.nounique + self.nonull = !!options.nonull + self.nosort = !!options.nosort + self.nocase = !!options.nocase + self.stat = !!options.stat + self.noprocess = !!options.noprocess + self.absolute = !!options.absolute + self.fs = options.fs || fs + + self.maxLength = options.maxLength || Infinity + self.cache = options.cache || Object.create(null) + self.statCache = options.statCache || Object.create(null) + self.symlinks = options.symlinks || Object.create(null) + + setupIgnores(self, options) + + self.changedCwd = false + var cwd = process.cwd() + if (!ownProp(options, "cwd")) + self.cwd = cwd + else { + self.cwd = path.resolve(options.cwd) + self.changedCwd = self.cwd !== cwd + } + + self.root = options.root || path.resolve(self.cwd, "/") + self.root = path.resolve(self.root) + if (process.platform === "win32") + self.root = self.root.replace(/\\/g, "/") + + // TODO: is an absolute `cwd` supposed to be resolved against `root`? + // e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test') + self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd) + if (process.platform === "win32") + self.cwdAbs = self.cwdAbs.replace(/\\/g, "/") + self.nomount = !!options.nomount + + // disable comments and negation in Minimatch. + // Note that they are not supported in Glob itself anyway. + options.nonegate = true + options.nocomment = true + // always treat \ in patterns as escapes, not path separators + options.allowWindowsEscape = false + + self.minimatch = new Minimatch(pattern, options) + self.options = self.minimatch.options +} + +function finish (self) { + var nou = self.nounique + var all = nou ? [] : Object.create(null) + + for (var i = 0, l = self.matches.length; i < l; i ++) { + var matches = self.matches[i] + if (!matches || Object.keys(matches).length === 0) { + if (self.nonull) { + // do like the shell, and spit out the literal glob + var literal = self.minimatch.globSet[i] + if (nou) + all.push(literal) + else + all[literal] = true + } + } else { + // had matches + var m = Object.keys(matches) + if (nou) + all.push.apply(all, m) + else + m.forEach(function (m) { + all[m] = true + }) + } + } + + if (!nou) + all = Object.keys(all) + + if (!self.nosort) + all = all.sort(alphasort) + + // at *some* point we statted all of these + if (self.mark) { + for (var i = 0; i < all.length; i++) { + all[i] = self._mark(all[i]) + } + if (self.nodir) { + all = all.filter(function (e) { + var notDir = !(/\/$/.test(e)) + var c = self.cache[e] || self.cache[makeAbs(self, e)] + if (notDir && c) + notDir = c !== 'DIR' && !Array.isArray(c) + return notDir + }) + } + } + + if (self.ignore.length) + all = all.filter(function(m) { + return !isIgnored(self, m) + }) + + self.found = all +} + +function mark (self, p) { + var abs = makeAbs(self, p) + var c = self.cache[abs] + var m = p + if (c) { + var isDir = c === 'DIR' || Array.isArray(c) + var slash = p.slice(-1) === '/' + + if (isDir && !slash) + m += '/' + else if (!isDir && slash) + m = m.slice(0, -1) + + if (m !== p) { + var mabs = makeAbs(self, m) + self.statCache[mabs] = self.statCache[abs] + self.cache[mabs] = self.cache[abs] + } + } + + return m +} + +// lotta situps... +function makeAbs (self, f) { + var abs = f + if (f.charAt(0) === '/') { + abs = path.join(self.root, f) + } else if (isAbsolute(f) || f === '') { + abs = f + } else if (self.changedCwd) { + abs = path.resolve(self.cwd, f) + } else { + abs = path.resolve(f) + } + + if (process.platform === 'win32') + abs = abs.replace(/\\/g, '/') + + return abs +} + + +// Return true, if pattern ends with globstar '**', for the accompanying parent directory. +// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents +function isIgnored (self, path) { + if (!self.ignore.length) + return false + + return self.ignore.some(function(item) { + return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) + }) +} + +function childrenIgnored (self, path) { + if (!self.ignore.length) + return false + + return self.ignore.some(function(item) { + return !!(item.gmatcher && item.gmatcher.match(path)) + }) +} diff --git a/node_modules/glob/glob.js b/node_modules/glob/glob.js new file mode 100644 index 0000000..37a4d7e --- /dev/null +++ b/node_modules/glob/glob.js @@ -0,0 +1,790 @@ +// Approach: +// +// 1. Get the minimatch set +// 2. For each pattern in the set, PROCESS(pattern, false) +// 3. Store matches per-set, then uniq them +// +// PROCESS(pattern, inGlobStar) +// Get the first [n] items from pattern that are all strings +// Join these together. This is PREFIX. +// If there is no more remaining, then stat(PREFIX) and +// add to matches if it succeeds. END. +// +// If inGlobStar and PREFIX is symlink and points to dir +// set ENTRIES = [] +// else readdir(PREFIX) as ENTRIES +// If fail, END +// +// with ENTRIES +// If pattern[n] is GLOBSTAR +// // handle the case where the globstar match is empty +// // by pruning it out, and testing the resulting pattern +// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) +// // handle other cases. +// for ENTRY in ENTRIES (not dotfiles) +// // attach globstar + tail onto the entry +// // Mark that this entry is a globstar match +// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) +// +// else // not globstar +// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) +// Test ENTRY against pattern[n] +// If fails, continue +// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) +// +// Caveat: +// Cache all stats and readdirs results to minimize syscall. Since all +// we ever care about is existence and directory-ness, we can just keep +// `true` for files, and [children,...] for directories, or `false` for +// things that don't exist. + +module.exports = glob + +var rp = require('fs.realpath') +var minimatch = require('minimatch') +var Minimatch = minimatch.Minimatch +var inherits = require('inherits') +var EE = require('events').EventEmitter +var path = require('path') +var assert = require('assert') +var isAbsolute = require('path-is-absolute') +var globSync = require('./sync.js') +var common = require('./common.js') +var setopts = common.setopts +var ownProp = common.ownProp +var inflight = require('inflight') +var util = require('util') +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored + +var once = require('once') + +function glob (pattern, options, cb) { + if (typeof options === 'function') cb = options, options = {} + if (!options) options = {} + + if (options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return globSync(pattern, options) + } + + return new Glob(pattern, options, cb) +} + +glob.sync = globSync +var GlobSync = glob.GlobSync = globSync.GlobSync + +// old api surface +glob.glob = glob + +function extend (origin, add) { + if (add === null || typeof add !== 'object') { + return origin + } + + var keys = Object.keys(add) + var i = keys.length + while (i--) { + origin[keys[i]] = add[keys[i]] + } + return origin +} + +glob.hasMagic = function (pattern, options_) { + var options = extend({}, options_) + options.noprocess = true + + var g = new Glob(pattern, options) + var set = g.minimatch.set + + if (!pattern) + return false + + if (set.length > 1) + return true + + for (var j = 0; j < set[0].length; j++) { + if (typeof set[0][j] !== 'string') + return true + } + + return false +} + +glob.Glob = Glob +inherits(Glob, EE) +function Glob (pattern, options, cb) { + if (typeof options === 'function') { + cb = options + options = null + } + + if (options && options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return new GlobSync(pattern, options) + } + + if (!(this instanceof Glob)) + return new Glob(pattern, options, cb) + + setopts(this, pattern, options) + this._didRealPath = false + + // process each pattern in the minimatch set + var n = this.minimatch.set.length + + // The matches are stored as {: true,...} so that + // duplicates are automagically pruned. + // Later, we do an Object.keys() on these. + // Keep them as a list so we can fill in when nonull is set. + this.matches = new Array(n) + + if (typeof cb === 'function') { + cb = once(cb) + this.on('error', cb) + this.on('end', function (matches) { + cb(null, matches) + }) + } + + var self = this + this._processing = 0 + + this._emitQueue = [] + this._processQueue = [] + this.paused = false + + if (this.noprocess) + return this + + if (n === 0) + return done() + + var sync = true + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false, done) + } + sync = false + + function done () { + --self._processing + if (self._processing <= 0) { + if (sync) { + process.nextTick(function () { + self._finish() + }) + } else { + self._finish() + } + } + } +} + +Glob.prototype._finish = function () { + assert(this instanceof Glob) + if (this.aborted) + return + + if (this.realpath && !this._didRealpath) + return this._realpath() + + common.finish(this) + this.emit('end', this.found) +} + +Glob.prototype._realpath = function () { + if (this._didRealpath) + return + + this._didRealpath = true + + var n = this.matches.length + if (n === 0) + return this._finish() + + var self = this + for (var i = 0; i < this.matches.length; i++) + this._realpathSet(i, next) + + function next () { + if (--n === 0) + self._finish() + } +} + +Glob.prototype._realpathSet = function (index, cb) { + var matchset = this.matches[index] + if (!matchset) + return cb() + + var found = Object.keys(matchset) + var self = this + var n = found.length + + if (n === 0) + return cb() + + var set = this.matches[index] = Object.create(null) + found.forEach(function (p, i) { + // If there's a problem with the stat, then it means that + // one or more of the links in the realpath couldn't be + // resolved. just return the abs value in that case. + p = self._makeAbs(p) + rp.realpath(p, self.realpathCache, function (er, real) { + if (!er) + set[real] = true + else if (er.syscall === 'stat') + set[p] = true + else + self.emit('error', er) // srsly wtf right here + + if (--n === 0) { + self.matches[index] = set + cb() + } + }) + }) +} + +Glob.prototype._mark = function (p) { + return common.mark(this, p) +} + +Glob.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} + +Glob.prototype.abort = function () { + this.aborted = true + this.emit('abort') +} + +Glob.prototype.pause = function () { + if (!this.paused) { + this.paused = true + this.emit('pause') + } +} + +Glob.prototype.resume = function () { + if (this.paused) { + this.emit('resume') + this.paused = false + if (this._emitQueue.length) { + var eq = this._emitQueue.slice(0) + this._emitQueue.length = 0 + for (var i = 0; i < eq.length; i ++) { + var e = eq[i] + this._emitMatch(e[0], e[1]) + } + } + if (this._processQueue.length) { + var pq = this._processQueue.slice(0) + this._processQueue.length = 0 + for (var i = 0; i < pq.length; i ++) { + var p = pq[i] + this._processing-- + this._process(p[0], p[1], p[2], p[3]) + } + } + } +} + +Glob.prototype._process = function (pattern, index, inGlobStar, cb) { + assert(this instanceof Glob) + assert(typeof cb === 'function') + + if (this.aborted) + return + + this._processing++ + if (this.paused) { + this._processQueue.push([pattern, index, inGlobStar, cb]) + return + } + + //console.error('PROCESS %d', this._processing, pattern) + + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. + + // see if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index, cb) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + + var remain = pattern.slice(n) + + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || + isAbsolute(pattern.map(function (p) { + return typeof p === 'string' ? p : '[*]' + }).join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + + var abs = this._makeAbs(read) + + //if ignored, skip _processing + if (childrenIgnored(this, read)) + return cb() + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) +} + +Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} + +Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + + // if the abs isn't a dir, then nothing can match! + if (!entries) + return cb() + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + + //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) + + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return cb() + + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return cb() + } + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + this._process([e].concat(remain), index, inGlobStar, cb) + } + cb() +} + +Glob.prototype._emitMatch = function (index, e) { + if (this.aborted) + return + + if (isIgnored(this, e)) + return + + if (this.paused) { + this._emitQueue.push([index, e]) + return + } + + var abs = isAbsolute(e) ? e : this._makeAbs(e) + + if (this.mark) + e = this._mark(e) + + if (this.absolute) + e = abs + + if (this.matches[index][e]) + return + + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } + + this.matches[index][e] = true + + var st = this.statCache[abs] + if (st) + this.emit('stat', e, st) + + this.emit('match', e) +} + +Glob.prototype._readdirInGlobStar = function (abs, cb) { + if (this.aborted) + return + + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false, cb) + + var lstatkey = 'lstat\0' + abs + var self = this + var lstatcb = inflight(lstatkey, lstatcb_) + + if (lstatcb) + self.fs.lstat(abs, lstatcb) + + function lstatcb_ (er, lstat) { + if (er && er.code === 'ENOENT') + return cb() + + var isSym = lstat && lstat.isSymbolicLink() + self.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && lstat && !lstat.isDirectory()) { + self.cache[abs] = 'FILE' + cb() + } else + self._readdir(abs, false, cb) + } +} + +Glob.prototype._readdir = function (abs, inGlobStar, cb) { + if (this.aborted) + return + + cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) + if (!cb) + return + + //console.error('RD %j %j', +inGlobStar, abs) + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs, cb) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return cb() + + if (Array.isArray(c)) + return cb(null, c) + } + + var self = this + self.fs.readdir(abs, readdirCb(this, abs, cb)) +} + +function readdirCb (self, abs, cb) { + return function (er, entries) { + if (er) + self._readdirError(abs, er, cb) + else + self._readdirEntries(abs, entries, cb) + } +} + +Glob.prototype._readdirEntries = function (abs, entries, cb) { + if (this.aborted) + return + + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + + this.cache[abs] = entries + return cb(null, entries) +} + +Glob.prototype._readdirError = function (f, er, cb) { + if (this.aborted) + return + + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + var abs = this._makeAbs(f) + this.cache[abs] = 'FILE' + if (abs === this.cwdAbs) { + var error = new Error(er.code + ' invalid cwd ' + this.cwd) + error.path = this.cwd + error.code = er.code + this.emit('error', error) + this.abort() + } + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) { + this.emit('error', er) + // If the error is handled, then we abort + // if not, we threw out of here + this.abort() + } + if (!this.silent) + console.error('glob error', er) + break + } + + return cb() +} + +Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} + + +Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + //console.error('pgs2', prefix, remain[0], entries) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return cb() + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false, cb) + + var isSym = this.symlinks[abs] + var len = entries.length + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return cb() + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true, cb) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true, cb) + } + + cb() +} + +Glob.prototype._processSimple = function (prefix, index, cb) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var self = this + this._stat(prefix, function (er, exists) { + self._processSimple2(prefix, index, er, exists, cb) + }) +} +Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { + + //console.error('ps2', prefix, exists) + + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + // If it doesn't exist, then just mark the lack of results + if (!exists) + return cb() + + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this._emitMatch(index, prefix) + cb() +} + +// Returns either 'DIR', 'FILE', or false +Glob.prototype._stat = function (f, cb) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + + if (f.length > this.maxLength) + return cb() + + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + + if (Array.isArray(c)) + c = 'DIR' + + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return cb(null, c) + + if (needDir && c === 'FILE') + return cb() + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (stat !== undefined) { + if (stat === false) + return cb(null, stat) + else { + var type = stat.isDirectory() ? 'DIR' : 'FILE' + if (needDir && type === 'FILE') + return cb() + else + return cb(null, type, stat) + } + } + + var self = this + var statcb = inflight('stat\0' + abs, lstatcb_) + if (statcb) + self.fs.lstat(abs, statcb) + + function lstatcb_ (er, lstat) { + if (lstat && lstat.isSymbolicLink()) { + // If it's a symlink, then treat it as the target, unless + // the target does not exist, then treat it as a file. + return self.fs.stat(abs, function (er, stat) { + if (er) + self._stat2(f, abs, null, lstat, cb) + else + self._stat2(f, abs, er, stat, cb) + }) + } else { + self._stat2(f, abs, er, lstat, cb) + } + } +} + +Glob.prototype._stat2 = function (f, abs, er, stat, cb) { + if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { + this.statCache[abs] = false + return cb() + } + + var needDir = f.slice(-1) === '/' + this.statCache[abs] = stat + + if (abs.slice(-1) === '/' && stat && !stat.isDirectory()) + return cb(null, false, stat) + + var c = true + if (stat) + c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c + + if (needDir && c === 'FILE') + return cb() + + return cb(null, c, stat) +} diff --git a/node_modules/glob/package.json b/node_modules/glob/package.json new file mode 100644 index 0000000..5940b64 --- /dev/null +++ b/node_modules/glob/package.json @@ -0,0 +1,55 @@ +{ + "author": "Isaac Z. Schlueter (http://blog.izs.me/)", + "name": "glob", + "description": "a little globber", + "version": "7.2.3", + "publishConfig": { + "tag": "v7-legacy" + }, + "repository": { + "type": "git", + "url": "git://github.com/isaacs/node-glob.git" + }, + "main": "glob.js", + "files": [ + "glob.js", + "sync.js", + "common.js" + ], + "engines": { + "node": "*" + }, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "devDependencies": { + "memfs": "^3.2.0", + "mkdirp": "0", + "rimraf": "^2.2.8", + "tap": "^15.0.6", + "tick": "0.0.6" + }, + "tap": { + "before": "test/00-setup.js", + "after": "test/zz-cleanup.js", + "jobs": 1 + }, + "scripts": { + "prepublish": "npm run benchclean", + "profclean": "rm -f v8.log profile.txt", + "test": "tap", + "test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js", + "bench": "bash benchmark.sh", + "prof": "bash prof.sh && cat profile.txt", + "benchclean": "node benchclean.js" + }, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } +} diff --git a/node_modules/glob/sync.js b/node_modules/glob/sync.js new file mode 100644 index 0000000..2c4f480 --- /dev/null +++ b/node_modules/glob/sync.js @@ -0,0 +1,486 @@ +module.exports = globSync +globSync.GlobSync = GlobSync + +var rp = require('fs.realpath') +var minimatch = require('minimatch') +var Minimatch = minimatch.Minimatch +var Glob = require('./glob.js').Glob +var util = require('util') +var path = require('path') +var assert = require('assert') +var isAbsolute = require('path-is-absolute') +var common = require('./common.js') +var setopts = common.setopts +var ownProp = common.ownProp +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored + +function globSync (pattern, options) { + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + + return new GlobSync(pattern, options).found +} + +function GlobSync (pattern, options) { + if (!pattern) + throw new Error('must provide pattern') + + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + + if (!(this instanceof GlobSync)) + return new GlobSync(pattern, options) + + setopts(this, pattern, options) + + if (this.noprocess) + return this + + var n = this.minimatch.set.length + this.matches = new Array(n) + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false) + } + this._finish() +} + +GlobSync.prototype._finish = function () { + assert.ok(this instanceof GlobSync) + if (this.realpath) { + var self = this + this.matches.forEach(function (matchset, index) { + var set = self.matches[index] = Object.create(null) + for (var p in matchset) { + try { + p = self._makeAbs(p) + var real = rp.realpathSync(p, self.realpathCache) + set[real] = true + } catch (er) { + if (er.syscall === 'stat') + set[self._makeAbs(p)] = true + else + throw er + } + } + }) + } + common.finish(this) +} + + +GlobSync.prototype._process = function (pattern, index, inGlobStar) { + assert.ok(this instanceof GlobSync) + + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. + + // See if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + + var remain = pattern.slice(n) + + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || + isAbsolute(pattern.map(function (p) { + return typeof p === 'string' ? p : '[*]' + }).join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + + var abs = this._makeAbs(read) + + //if ignored, skip processing + if (childrenIgnored(this, read)) + return + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar) +} + + +GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { + var entries = this._readdir(abs, inGlobStar) + + // if the abs isn't a dir, then nothing can match! + if (!entries) + return + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return + + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix.slice(-1) !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return + } + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) + newPattern = [prefix, e] + else + newPattern = [e] + this._process(newPattern.concat(remain), index, inGlobStar) + } +} + + +GlobSync.prototype._emitMatch = function (index, e) { + if (isIgnored(this, e)) + return + + var abs = this._makeAbs(e) + + if (this.mark) + e = this._mark(e) + + if (this.absolute) { + e = abs + } + + if (this.matches[index][e]) + return + + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } + + this.matches[index][e] = true + + if (this.stat) + this._stat(e) +} + + +GlobSync.prototype._readdirInGlobStar = function (abs) { + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false) + + var entries + var lstat + var stat + try { + lstat = this.fs.lstatSync(abs) + } catch (er) { + if (er.code === 'ENOENT') { + // lstat failed, doesn't exist + return null + } + } + + var isSym = lstat && lstat.isSymbolicLink() + this.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && lstat && !lstat.isDirectory()) + this.cache[abs] = 'FILE' + else + entries = this._readdir(abs, false) + + return entries +} + +GlobSync.prototype._readdir = function (abs, inGlobStar) { + var entries + + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return null + + if (Array.isArray(c)) + return c + } + + try { + return this._readdirEntries(abs, this.fs.readdirSync(abs)) + } catch (er) { + this._readdirError(abs, er) + return null + } +} + +GlobSync.prototype._readdirEntries = function (abs, entries) { + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + + this.cache[abs] = entries + + // mark and cache dir-ness + return entries +} + +GlobSync.prototype._readdirError = function (f, er) { + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + var abs = this._makeAbs(f) + this.cache[abs] = 'FILE' + if (abs === this.cwdAbs) { + var error = new Error(er.code + ' invalid cwd ' + this.cwd) + error.path = this.cwd + error.code = er.code + throw error + } + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) + throw er + if (!this.silent) + console.error('glob error', er) + break + } +} + +GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { + + var entries = this._readdir(abs, inGlobStar) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false) + + var len = entries.length + var isSym = this.symlinks[abs] + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true) + } +} + +GlobSync.prototype._processSimple = function (prefix, index) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var exists = this._stat(prefix) + + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + // If it doesn't exist, then just mark the lack of results + if (!exists) + return + + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this._emitMatch(index, prefix) +} + +// Returns either 'DIR', 'FILE', or false +GlobSync.prototype._stat = function (f) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + + if (f.length > this.maxLength) + return false + + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + + if (Array.isArray(c)) + c = 'DIR' + + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return c + + if (needDir && c === 'FILE') + return false + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (!stat) { + var lstat + try { + lstat = this.fs.lstatSync(abs) + } catch (er) { + if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { + this.statCache[abs] = false + return false + } + } + + if (lstat && lstat.isSymbolicLink()) { + try { + stat = this.fs.statSync(abs) + } catch (er) { + stat = lstat + } + } else { + stat = lstat + } + } + + this.statCache[abs] = stat + + var c = true + if (stat) + c = stat.isDirectory() ? 'DIR' : 'FILE' + + this.cache[abs] = this.cache[abs] || c + + if (needDir && c === 'FILE') + return false + + return c +} + +GlobSync.prototype._mark = function (p) { + return common.mark(this, p) +} + +GlobSync.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} diff --git a/node_modules/graceful-fs/LICENSE b/node_modules/graceful-fs/LICENSE new file mode 100644 index 0000000..e906a25 --- /dev/null +++ b/node_modules/graceful-fs/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) 2011-2022 Isaac Z. Schlueter, Ben Noordhuis, and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/graceful-fs/README.md b/node_modules/graceful-fs/README.md new file mode 100644 index 0000000..82d6e4d --- /dev/null +++ b/node_modules/graceful-fs/README.md @@ -0,0 +1,143 @@ +# graceful-fs + +graceful-fs functions as a drop-in replacement for the fs module, +making various improvements. + +The improvements are meant to normalize behavior across different +platforms and environments, and to make filesystem access more +resilient to errors. + +## Improvements over [fs module](https://nodejs.org/api/fs.html) + +* Queues up `open` and `readdir` calls, and retries them once + something closes if there is an EMFILE error from too many file + descriptors. +* fixes `lchmod` for Node versions prior to 0.6.2. +* implements `fs.lutimes` if possible. Otherwise it becomes a noop. +* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or + `lchown` if the user isn't root. +* makes `lchmod` and `lchown` become noops, if not available. +* retries reading a file if `read` results in EAGAIN error. + +On Windows, it retries renaming a file for up to one second if `EACCESS` +or `EPERM` error occurs, likely because antivirus software has locked +the directory. + +## USAGE + +```javascript +// use just like fs +var fs = require('graceful-fs') + +// now go and do stuff with it... +fs.readFile('some-file-or-whatever', (err, data) => { + // Do stuff here. +}) +``` + +## Sync methods + +This module cannot intercept or handle `EMFILE` or `ENFILE` errors from sync +methods. If you use sync methods which open file descriptors then you are +responsible for dealing with any errors. + +This is a known limitation, not a bug. + +## Global Patching + +If you want to patch the global fs module (or any other fs-like +module) you can do this: + +```javascript +// Make sure to read the caveat below. +var realFs = require('fs') +var gracefulFs = require('graceful-fs') +gracefulFs.gracefulify(realFs) +``` + +This should only ever be done at the top-level application layer, in +order to delay on EMFILE errors from any fs-using dependencies. You +should **not** do this in a library, because it can cause unexpected +delays in other parts of the program. + +## Changes + +This module is fairly stable at this point, and used by a lot of +things. That being said, because it implements a subtle behavior +change in a core part of the node API, even modest changes can be +extremely breaking, and the versioning is thus biased towards +bumping the major when in doubt. + +The main change between major versions has been switching between +providing a fully-patched `fs` module vs monkey-patching the node core +builtin, and the approach by which a non-monkey-patched `fs` was +created. + +The goal is to trade `EMFILE` errors for slower fs operations. So, if +you try to open a zillion files, rather than crashing, `open` +operations will be queued up and wait for something else to `close`. + +There are advantages to each approach. Monkey-patching the fs means +that no `EMFILE` errors can possibly occur anywhere in your +application, because everything is using the same core `fs` module, +which is patched. However, it can also obviously cause undesirable +side-effects, especially if the module is loaded multiple times. + +Implementing a separate-but-identical patched `fs` module is more +surgical (and doesn't run the risk of patching multiple times), but +also imposes the challenge of keeping in sync with the core module. + +The current approach loads the `fs` module, and then creates a +lookalike object that has all the same methods, except a few that are +patched. It is safe to use in all versions of Node from 0.8 through +7.0. + +### v4 + +* Do not monkey-patch the fs module. This module may now be used as a + drop-in dep, and users can opt into monkey-patching the fs builtin + if their app requires it. + +### v3 + +* Monkey-patch fs, because the eval approach no longer works on recent + node. +* fixed possible type-error throw if rename fails on windows +* verify that we *never* get EMFILE errors +* Ignore ENOSYS from chmod/chown +* clarify that graceful-fs must be used as a drop-in + +### v2.1.0 + +* Use eval rather than monkey-patching fs. +* readdir: Always sort the results +* win32: requeue a file if error has an OK status + +### v2.0 + +* A return to monkey patching +* wrap process.cwd + +### v1.1 + +* wrap readFile +* Wrap fs.writeFile. +* readdir protection +* Don't clobber the fs builtin +* Handle fs.read EAGAIN errors by trying again +* Expose the curOpen counter +* No-op lchown/lchmod if not implemented +* fs.rename patch only for win32 +* Patch fs.rename to handle AV software on Windows +* Close #4 Chown should not fail on einval or eperm if non-root +* Fix isaacs/fstream#1 Only wrap fs one time +* Fix #3 Start at 1024 max files, then back off on EMFILE +* lutimes that doens't blow up on Linux +* A full on-rewrite using a queue instead of just swallowing the EMFILE error +* Wrap Read/Write streams as well + +### 1.0 + +* Update engines for node 0.6 +* Be lstat-graceful on Windows +* first diff --git a/node_modules/graceful-fs/clone.js b/node_modules/graceful-fs/clone.js new file mode 100644 index 0000000..dff3cc8 --- /dev/null +++ b/node_modules/graceful-fs/clone.js @@ -0,0 +1,23 @@ +'use strict' + +module.exports = clone + +var getPrototypeOf = Object.getPrototypeOf || function (obj) { + return obj.__proto__ +} + +function clone (obj) { + if (obj === null || typeof obj !== 'object') + return obj + + if (obj instanceof Object) + var copy = { __proto__: getPrototypeOf(obj) } + else + var copy = Object.create(null) + + Object.getOwnPropertyNames(obj).forEach(function (key) { + Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key)) + }) + + return copy +} diff --git a/node_modules/graceful-fs/graceful-fs.js b/node_modules/graceful-fs/graceful-fs.js new file mode 100644 index 0000000..8d5b89e --- /dev/null +++ b/node_modules/graceful-fs/graceful-fs.js @@ -0,0 +1,448 @@ +var fs = require('fs') +var polyfills = require('./polyfills.js') +var legacy = require('./legacy-streams.js') +var clone = require('./clone.js') + +var util = require('util') + +/* istanbul ignore next - node 0.x polyfill */ +var gracefulQueue +var previousSymbol + +/* istanbul ignore else - node 0.x polyfill */ +if (typeof Symbol === 'function' && typeof Symbol.for === 'function') { + gracefulQueue = Symbol.for('graceful-fs.queue') + // This is used in testing by future versions + previousSymbol = Symbol.for('graceful-fs.previous') +} else { + gracefulQueue = '___graceful-fs.queue' + previousSymbol = '___graceful-fs.previous' +} + +function noop () {} + +function publishQueue(context, queue) { + Object.defineProperty(context, gracefulQueue, { + get: function() { + return queue + } + }) +} + +var debug = noop +if (util.debuglog) + debug = util.debuglog('gfs4') +else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) + debug = function() { + var m = util.format.apply(util, arguments) + m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ') + console.error(m) + } + +// Once time initialization +if (!fs[gracefulQueue]) { + // This queue can be shared by multiple loaded instances + var queue = global[gracefulQueue] || [] + publishQueue(fs, queue) + + // Patch fs.close/closeSync to shared queue version, because we need + // to retry() whenever a close happens *anywhere* in the program. + // This is essential when multiple graceful-fs instances are + // in play at the same time. + fs.close = (function (fs$close) { + function close (fd, cb) { + return fs$close.call(fs, fd, function (err) { + // This function uses the graceful-fs shared queue + if (!err) { + resetQueue() + } + + if (typeof cb === 'function') + cb.apply(this, arguments) + }) + } + + Object.defineProperty(close, previousSymbol, { + value: fs$close + }) + return close + })(fs.close) + + fs.closeSync = (function (fs$closeSync) { + function closeSync (fd) { + // This function uses the graceful-fs shared queue + fs$closeSync.apply(fs, arguments) + resetQueue() + } + + Object.defineProperty(closeSync, previousSymbol, { + value: fs$closeSync + }) + return closeSync + })(fs.closeSync) + + if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) { + process.on('exit', function() { + debug(fs[gracefulQueue]) + require('assert').equal(fs[gracefulQueue].length, 0) + }) + } +} + +if (!global[gracefulQueue]) { + publishQueue(global, fs[gracefulQueue]); +} + +module.exports = patch(clone(fs)) +if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) { + module.exports = patch(fs) + fs.__patched = true; +} + +function patch (fs) { + // Everything that references the open() function needs to be in here + polyfills(fs) + fs.gracefulify = patch + + fs.createReadStream = createReadStream + fs.createWriteStream = createWriteStream + var fs$readFile = fs.readFile + fs.readFile = readFile + function readFile (path, options, cb) { + if (typeof options === 'function') + cb = options, options = null + + return go$readFile(path, options, cb) + + function go$readFile (path, options, cb, startTime) { + return fs$readFile(path, options, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$readFile, [path, options, cb], err, startTime || Date.now(), Date.now()]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + } + }) + } + } + + var fs$writeFile = fs.writeFile + fs.writeFile = writeFile + function writeFile (path, data, options, cb) { + if (typeof options === 'function') + cb = options, options = null + + return go$writeFile(path, data, options, cb) + + function go$writeFile (path, data, options, cb, startTime) { + return fs$writeFile(path, data, options, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$writeFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + } + }) + } + } + + var fs$appendFile = fs.appendFile + if (fs$appendFile) + fs.appendFile = appendFile + function appendFile (path, data, options, cb) { + if (typeof options === 'function') + cb = options, options = null + + return go$appendFile(path, data, options, cb) + + function go$appendFile (path, data, options, cb, startTime) { + return fs$appendFile(path, data, options, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$appendFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + } + }) + } + } + + var fs$copyFile = fs.copyFile + if (fs$copyFile) + fs.copyFile = copyFile + function copyFile (src, dest, flags, cb) { + if (typeof flags === 'function') { + cb = flags + flags = 0 + } + return go$copyFile(src, dest, flags, cb) + + function go$copyFile (src, dest, flags, cb, startTime) { + return fs$copyFile(src, dest, flags, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$copyFile, [src, dest, flags, cb], err, startTime || Date.now(), Date.now()]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + } + }) + } + } + + var fs$readdir = fs.readdir + fs.readdir = readdir + var noReaddirOptionVersions = /^v[0-5]\./ + function readdir (path, options, cb) { + if (typeof options === 'function') + cb = options, options = null + + var go$readdir = noReaddirOptionVersions.test(process.version) + ? function go$readdir (path, options, cb, startTime) { + return fs$readdir(path, fs$readdirCallback( + path, options, cb, startTime + )) + } + : function go$readdir (path, options, cb, startTime) { + return fs$readdir(path, options, fs$readdirCallback( + path, options, cb, startTime + )) + } + + return go$readdir(path, options, cb) + + function fs$readdirCallback (path, options, cb, startTime) { + return function (err, files) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([ + go$readdir, + [path, options, cb], + err, + startTime || Date.now(), + Date.now() + ]) + else { + if (files && files.sort) + files.sort() + + if (typeof cb === 'function') + cb.call(this, err, files) + } + } + } + } + + if (process.version.substr(0, 4) === 'v0.8') { + var legStreams = legacy(fs) + ReadStream = legStreams.ReadStream + WriteStream = legStreams.WriteStream + } + + var fs$ReadStream = fs.ReadStream + if (fs$ReadStream) { + ReadStream.prototype = Object.create(fs$ReadStream.prototype) + ReadStream.prototype.open = ReadStream$open + } + + var fs$WriteStream = fs.WriteStream + if (fs$WriteStream) { + WriteStream.prototype = Object.create(fs$WriteStream.prototype) + WriteStream.prototype.open = WriteStream$open + } + + Object.defineProperty(fs, 'ReadStream', { + get: function () { + return ReadStream + }, + set: function (val) { + ReadStream = val + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(fs, 'WriteStream', { + get: function () { + return WriteStream + }, + set: function (val) { + WriteStream = val + }, + enumerable: true, + configurable: true + }) + + // legacy names + var FileReadStream = ReadStream + Object.defineProperty(fs, 'FileReadStream', { + get: function () { + return FileReadStream + }, + set: function (val) { + FileReadStream = val + }, + enumerable: true, + configurable: true + }) + var FileWriteStream = WriteStream + Object.defineProperty(fs, 'FileWriteStream', { + get: function () { + return FileWriteStream + }, + set: function (val) { + FileWriteStream = val + }, + enumerable: true, + configurable: true + }) + + function ReadStream (path, options) { + if (this instanceof ReadStream) + return fs$ReadStream.apply(this, arguments), this + else + return ReadStream.apply(Object.create(ReadStream.prototype), arguments) + } + + function ReadStream$open () { + var that = this + open(that.path, that.flags, that.mode, function (err, fd) { + if (err) { + if (that.autoClose) + that.destroy() + + that.emit('error', err) + } else { + that.fd = fd + that.emit('open', fd) + that.read() + } + }) + } + + function WriteStream (path, options) { + if (this instanceof WriteStream) + return fs$WriteStream.apply(this, arguments), this + else + return WriteStream.apply(Object.create(WriteStream.prototype), arguments) + } + + function WriteStream$open () { + var that = this + open(that.path, that.flags, that.mode, function (err, fd) { + if (err) { + that.destroy() + that.emit('error', err) + } else { + that.fd = fd + that.emit('open', fd) + } + }) + } + + function createReadStream (path, options) { + return new fs.ReadStream(path, options) + } + + function createWriteStream (path, options) { + return new fs.WriteStream(path, options) + } + + var fs$open = fs.open + fs.open = open + function open (path, flags, mode, cb) { + if (typeof mode === 'function') + cb = mode, mode = null + + return go$open(path, flags, mode, cb) + + function go$open (path, flags, mode, cb, startTime) { + return fs$open(path, flags, mode, function (err, fd) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$open, [path, flags, mode, cb], err, startTime || Date.now(), Date.now()]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + } + }) + } + } + + return fs +} + +function enqueue (elem) { + debug('ENQUEUE', elem[0].name, elem[1]) + fs[gracefulQueue].push(elem) + retry() +} + +// keep track of the timeout between retry() calls +var retryTimer + +// reset the startTime and lastTime to now +// this resets the start of the 60 second overall timeout as well as the +// delay between attempts so that we'll retry these jobs sooner +function resetQueue () { + var now = Date.now() + for (var i = 0; i < fs[gracefulQueue].length; ++i) { + // entries that are only a length of 2 are from an older version, don't + // bother modifying those since they'll be retried anyway. + if (fs[gracefulQueue][i].length > 2) { + fs[gracefulQueue][i][3] = now // startTime + fs[gracefulQueue][i][4] = now // lastTime + } + } + // call retry to make sure we're actively processing the queue + retry() +} + +function retry () { + // clear the timer and remove it to help prevent unintended concurrency + clearTimeout(retryTimer) + retryTimer = undefined + + if (fs[gracefulQueue].length === 0) + return + + var elem = fs[gracefulQueue].shift() + var fn = elem[0] + var args = elem[1] + // these items may be unset if they were added by an older graceful-fs + var err = elem[2] + var startTime = elem[3] + var lastTime = elem[4] + + // if we don't have a startTime we have no way of knowing if we've waited + // long enough, so go ahead and retry this item now + if (startTime === undefined) { + debug('RETRY', fn.name, args) + fn.apply(null, args) + } else if (Date.now() - startTime >= 60000) { + // it's been more than 60 seconds total, bail now + debug('TIMEOUT', fn.name, args) + var cb = args.pop() + if (typeof cb === 'function') + cb.call(null, err) + } else { + // the amount of time between the last attempt and right now + var sinceAttempt = Date.now() - lastTime + // the amount of time between when we first tried, and when we last tried + // rounded up to at least 1 + var sinceStart = Math.max(lastTime - startTime, 1) + // backoff. wait longer than the total time we've been retrying, but only + // up to a maximum of 100ms + var desiredDelay = Math.min(sinceStart * 1.2, 100) + // it's been long enough since the last retry, do it again + if (sinceAttempt >= desiredDelay) { + debug('RETRY', fn.name, args) + fn.apply(null, args.concat([startTime])) + } else { + // if we can't do this job yet, push it to the end of the queue + // and let the next iteration check again + fs[gracefulQueue].push(elem) + } + } + + // schedule our next run if one isn't already scheduled + if (retryTimer === undefined) { + retryTimer = setTimeout(retry, 0) + } +} diff --git a/node_modules/graceful-fs/legacy-streams.js b/node_modules/graceful-fs/legacy-streams.js new file mode 100644 index 0000000..d617b50 --- /dev/null +++ b/node_modules/graceful-fs/legacy-streams.js @@ -0,0 +1,118 @@ +var Stream = require('stream').Stream + +module.exports = legacy + +function legacy (fs) { + return { + ReadStream: ReadStream, + WriteStream: WriteStream + } + + function ReadStream (path, options) { + if (!(this instanceof ReadStream)) return new ReadStream(path, options); + + Stream.call(this); + + var self = this; + + this.path = path; + this.fd = null; + this.readable = true; + this.paused = false; + + this.flags = 'r'; + this.mode = 438; /*=0666*/ + this.bufferSize = 64 * 1024; + + options = options || {}; + + // Mixin options into this + var keys = Object.keys(options); + for (var index = 0, length = keys.length; index < length; index++) { + var key = keys[index]; + this[key] = options[key]; + } + + if (this.encoding) this.setEncoding(this.encoding); + + if (this.start !== undefined) { + if ('number' !== typeof this.start) { + throw TypeError('start must be a Number'); + } + if (this.end === undefined) { + this.end = Infinity; + } else if ('number' !== typeof this.end) { + throw TypeError('end must be a Number'); + } + + if (this.start > this.end) { + throw new Error('start must be <= end'); + } + + this.pos = this.start; + } + + if (this.fd !== null) { + process.nextTick(function() { + self._read(); + }); + return; + } + + fs.open(this.path, this.flags, this.mode, function (err, fd) { + if (err) { + self.emit('error', err); + self.readable = false; + return; + } + + self.fd = fd; + self.emit('open', fd); + self._read(); + }) + } + + function WriteStream (path, options) { + if (!(this instanceof WriteStream)) return new WriteStream(path, options); + + Stream.call(this); + + this.path = path; + this.fd = null; + this.writable = true; + + this.flags = 'w'; + this.encoding = 'binary'; + this.mode = 438; /*=0666*/ + this.bytesWritten = 0; + + options = options || {}; + + // Mixin options into this + var keys = Object.keys(options); + for (var index = 0, length = keys.length; index < length; index++) { + var key = keys[index]; + this[key] = options[key]; + } + + if (this.start !== undefined) { + if ('number' !== typeof this.start) { + throw TypeError('start must be a Number'); + } + if (this.start < 0) { + throw new Error('start must be >= zero'); + } + + this.pos = this.start; + } + + this.busy = false; + this._queue = []; + + if (this.fd === null) { + this._open = fs.open; + this._queue.push([this._open, this.path, this.flags, this.mode, undefined]); + this.flush(); + } + } +} diff --git a/node_modules/graceful-fs/package.json b/node_modules/graceful-fs/package.json new file mode 100644 index 0000000..87babf0 --- /dev/null +++ b/node_modules/graceful-fs/package.json @@ -0,0 +1,53 @@ +{ + "name": "graceful-fs", + "description": "A drop-in replacement for fs, making various improvements.", + "version": "4.2.11", + "repository": { + "type": "git", + "url": "https://github.com/isaacs/node-graceful-fs" + }, + "main": "graceful-fs.js", + "directories": { + "test": "test" + }, + "scripts": { + "preversion": "npm test", + "postversion": "npm publish", + "postpublish": "git push origin --follow-tags", + "test": "nyc --silent node test.js | tap -c -", + "posttest": "nyc report" + }, + "keywords": [ + "fs", + "module", + "reading", + "retry", + "retries", + "queue", + "error", + "errors", + "handling", + "EMFILE", + "EAGAIN", + "EINVAL", + "EPERM", + "EACCESS" + ], + "license": "ISC", + "devDependencies": { + "import-fresh": "^2.0.0", + "mkdirp": "^0.5.0", + "rimraf": "^2.2.8", + "tap": "^16.3.4" + }, + "files": [ + "fs.js", + "graceful-fs.js", + "legacy-streams.js", + "polyfills.js", + "clone.js" + ], + "tap": { + "reporter": "classic" + } +} diff --git a/node_modules/graceful-fs/polyfills.js b/node_modules/graceful-fs/polyfills.js new file mode 100644 index 0000000..453f1a9 --- /dev/null +++ b/node_modules/graceful-fs/polyfills.js @@ -0,0 +1,355 @@ +var constants = require('constants') + +var origCwd = process.cwd +var cwd = null + +var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform + +process.cwd = function() { + if (!cwd) + cwd = origCwd.call(process) + return cwd +} +try { + process.cwd() +} catch (er) {} + +// This check is needed until node.js 12 is required +if (typeof process.chdir === 'function') { + var chdir = process.chdir + process.chdir = function (d) { + cwd = null + chdir.call(process, d) + } + if (Object.setPrototypeOf) Object.setPrototypeOf(process.chdir, chdir) +} + +module.exports = patch + +function patch (fs) { + // (re-)implement some things that are known busted or missing. + + // lchmod, broken prior to 0.6.2 + // back-port the fix here. + if (constants.hasOwnProperty('O_SYMLINK') && + process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) { + patchLchmod(fs) + } + + // lutimes implementation, or no-op + if (!fs.lutimes) { + patchLutimes(fs) + } + + // https://github.com/isaacs/node-graceful-fs/issues/4 + // Chown should not fail on einval or eperm if non-root. + // It should not fail on enosys ever, as this just indicates + // that a fs doesn't support the intended operation. + + fs.chown = chownFix(fs.chown) + fs.fchown = chownFix(fs.fchown) + fs.lchown = chownFix(fs.lchown) + + fs.chmod = chmodFix(fs.chmod) + fs.fchmod = chmodFix(fs.fchmod) + fs.lchmod = chmodFix(fs.lchmod) + + fs.chownSync = chownFixSync(fs.chownSync) + fs.fchownSync = chownFixSync(fs.fchownSync) + fs.lchownSync = chownFixSync(fs.lchownSync) + + fs.chmodSync = chmodFixSync(fs.chmodSync) + fs.fchmodSync = chmodFixSync(fs.fchmodSync) + fs.lchmodSync = chmodFixSync(fs.lchmodSync) + + fs.stat = statFix(fs.stat) + fs.fstat = statFix(fs.fstat) + fs.lstat = statFix(fs.lstat) + + fs.statSync = statFixSync(fs.statSync) + fs.fstatSync = statFixSync(fs.fstatSync) + fs.lstatSync = statFixSync(fs.lstatSync) + + // if lchmod/lchown do not exist, then make them no-ops + if (fs.chmod && !fs.lchmod) { + fs.lchmod = function (path, mode, cb) { + if (cb) process.nextTick(cb) + } + fs.lchmodSync = function () {} + } + if (fs.chown && !fs.lchown) { + fs.lchown = function (path, uid, gid, cb) { + if (cb) process.nextTick(cb) + } + fs.lchownSync = function () {} + } + + // on Windows, A/V software can lock the directory, causing this + // to fail with an EACCES or EPERM if the directory contains newly + // created files. Try again on failure, for up to 60 seconds. + + // Set the timeout this long because some Windows Anti-Virus, such as Parity + // bit9, may lock files for up to a minute, causing npm package install + // failures. Also, take care to yield the scheduler. Windows scheduling gives + // CPU to a busy looping process, which can cause the program causing the lock + // contention to be starved of CPU by node, so the contention doesn't resolve. + if (platform === "win32") { + fs.rename = typeof fs.rename !== 'function' ? fs.rename + : (function (fs$rename) { + function rename (from, to, cb) { + var start = Date.now() + var backoff = 0; + fs$rename(from, to, function CB (er) { + if (er + && (er.code === "EACCES" || er.code === "EPERM" || er.code === "EBUSY") + && Date.now() - start < 60000) { + setTimeout(function() { + fs.stat(to, function (stater, st) { + if (stater && stater.code === "ENOENT") + fs$rename(from, to, CB); + else + cb(er) + }) + }, backoff) + if (backoff < 100) + backoff += 10; + return; + } + if (cb) cb(er) + }) + } + if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename) + return rename + })(fs.rename) + } + + // if read() returns EAGAIN, then just try it again. + fs.read = typeof fs.read !== 'function' ? fs.read + : (function (fs$read) { + function read (fd, buffer, offset, length, position, callback_) { + var callback + if (callback_ && typeof callback_ === 'function') { + var eagCounter = 0 + callback = function (er, _, __) { + if (er && er.code === 'EAGAIN' && eagCounter < 10) { + eagCounter ++ + return fs$read.call(fs, fd, buffer, offset, length, position, callback) + } + callback_.apply(this, arguments) + } + } + return fs$read.call(fs, fd, buffer, offset, length, position, callback) + } + + // This ensures `util.promisify` works as it does for native `fs.read`. + if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read) + return read + })(fs.read) + + fs.readSync = typeof fs.readSync !== 'function' ? fs.readSync + : (function (fs$readSync) { return function (fd, buffer, offset, length, position) { + var eagCounter = 0 + while (true) { + try { + return fs$readSync.call(fs, fd, buffer, offset, length, position) + } catch (er) { + if (er.code === 'EAGAIN' && eagCounter < 10) { + eagCounter ++ + continue + } + throw er + } + } + }})(fs.readSync) + + function patchLchmod (fs) { + fs.lchmod = function (path, mode, callback) { + fs.open( path + , constants.O_WRONLY | constants.O_SYMLINK + , mode + , function (err, fd) { + if (err) { + if (callback) callback(err) + return + } + // prefer to return the chmod error, if one occurs, + // but still try to close, and report closing errors if they occur. + fs.fchmod(fd, mode, function (err) { + fs.close(fd, function(err2) { + if (callback) callback(err || err2) + }) + }) + }) + } + + fs.lchmodSync = function (path, mode) { + var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode) + + // prefer to return the chmod error, if one occurs, + // but still try to close, and report closing errors if they occur. + var threw = true + var ret + try { + ret = fs.fchmodSync(fd, mode) + threw = false + } finally { + if (threw) { + try { + fs.closeSync(fd) + } catch (er) {} + } else { + fs.closeSync(fd) + } + } + return ret + } + } + + function patchLutimes (fs) { + if (constants.hasOwnProperty("O_SYMLINK") && fs.futimes) { + fs.lutimes = function (path, at, mt, cb) { + fs.open(path, constants.O_SYMLINK, function (er, fd) { + if (er) { + if (cb) cb(er) + return + } + fs.futimes(fd, at, mt, function (er) { + fs.close(fd, function (er2) { + if (cb) cb(er || er2) + }) + }) + }) + } + + fs.lutimesSync = function (path, at, mt) { + var fd = fs.openSync(path, constants.O_SYMLINK) + var ret + var threw = true + try { + ret = fs.futimesSync(fd, at, mt) + threw = false + } finally { + if (threw) { + try { + fs.closeSync(fd) + } catch (er) {} + } else { + fs.closeSync(fd) + } + } + return ret + } + + } else if (fs.futimes) { + fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) } + fs.lutimesSync = function () {} + } + } + + function chmodFix (orig) { + if (!orig) return orig + return function (target, mode, cb) { + return orig.call(fs, target, mode, function (er) { + if (chownErOk(er)) er = null + if (cb) cb.apply(this, arguments) + }) + } + } + + function chmodFixSync (orig) { + if (!orig) return orig + return function (target, mode) { + try { + return orig.call(fs, target, mode) + } catch (er) { + if (!chownErOk(er)) throw er + } + } + } + + + function chownFix (orig) { + if (!orig) return orig + return function (target, uid, gid, cb) { + return orig.call(fs, target, uid, gid, function (er) { + if (chownErOk(er)) er = null + if (cb) cb.apply(this, arguments) + }) + } + } + + function chownFixSync (orig) { + if (!orig) return orig + return function (target, uid, gid) { + try { + return orig.call(fs, target, uid, gid) + } catch (er) { + if (!chownErOk(er)) throw er + } + } + } + + function statFix (orig) { + if (!orig) return orig + // Older versions of Node erroneously returned signed integers for + // uid + gid. + return function (target, options, cb) { + if (typeof options === 'function') { + cb = options + options = null + } + function callback (er, stats) { + if (stats) { + if (stats.uid < 0) stats.uid += 0x100000000 + if (stats.gid < 0) stats.gid += 0x100000000 + } + if (cb) cb.apply(this, arguments) + } + return options ? orig.call(fs, target, options, callback) + : orig.call(fs, target, callback) + } + } + + function statFixSync (orig) { + if (!orig) return orig + // Older versions of Node erroneously returned signed integers for + // uid + gid. + return function (target, options) { + var stats = options ? orig.call(fs, target, options) + : orig.call(fs, target) + if (stats) { + if (stats.uid < 0) stats.uid += 0x100000000 + if (stats.gid < 0) stats.gid += 0x100000000 + } + return stats; + } + } + + // ENOSYS means that the fs doesn't support the op. Just ignore + // that, because it doesn't matter. + // + // if there's no getuid, or if getuid() is something other + // than 0, and the error is EINVAL or EPERM, then just ignore + // it. + // + // This specific case is a silent failure in cp, install, tar, + // and most other unix tools that manage permissions. + // + // When running as root, or if other types of errors are + // encountered, then it's strict. + function chownErOk (er) { + if (!er) + return true + + if (er.code === "ENOSYS") + return true + + var nonroot = !process.getuid || process.getuid() !== 0 + if (nonroot) { + if (er.code === "EINVAL" || er.code === "EPERM") + return true + } + + return false + } +} diff --git a/node_modules/inflight/LICENSE b/node_modules/inflight/LICENSE new file mode 100644 index 0000000..05eeeb8 --- /dev/null +++ b/node_modules/inflight/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/inflight/README.md b/node_modules/inflight/README.md new file mode 100644 index 0000000..6dc8929 --- /dev/null +++ b/node_modules/inflight/README.md @@ -0,0 +1,37 @@ +# inflight + +Add callbacks to requests in flight to avoid async duplication + +## USAGE + +```javascript +var inflight = require('inflight') + +// some request that does some stuff +function req(key, callback) { + // key is any random string. like a url or filename or whatever. + // + // will return either a falsey value, indicating that the + // request for this key is already in flight, or a new callback + // which when called will call all callbacks passed to inflightk + // with the same key + callback = inflight(key, callback) + + // If we got a falsey value back, then there's already a req going + if (!callback) return + + // this is where you'd fetch the url or whatever + // callback is also once()-ified, so it can safely be assigned + // to multiple events etc. First call wins. + setTimeout(function() { + callback(null, key) + }, 100) +} + +// only assigns a single setTimeout +// when it dings, all cbs get called +req('foo', cb1) +req('foo', cb2) +req('foo', cb3) +req('foo', cb4) +``` diff --git a/node_modules/inflight/inflight.js b/node_modules/inflight/inflight.js new file mode 100644 index 0000000..48202b3 --- /dev/null +++ b/node_modules/inflight/inflight.js @@ -0,0 +1,54 @@ +var wrappy = require('wrappy') +var reqs = Object.create(null) +var once = require('once') + +module.exports = wrappy(inflight) + +function inflight (key, cb) { + if (reqs[key]) { + reqs[key].push(cb) + return null + } else { + reqs[key] = [cb] + return makeres(key) + } +} + +function makeres (key) { + return once(function RES () { + var cbs = reqs[key] + var len = cbs.length + var args = slice(arguments) + + // XXX It's somewhat ambiguous whether a new callback added in this + // pass should be queued for later execution if something in the + // list of callbacks throws, or if it should just be discarded. + // However, it's such an edge case that it hardly matters, and either + // choice is likely as surprising as the other. + // As it happens, we do go ahead and schedule it for later execution. + try { + for (var i = 0; i < len; i++) { + cbs[i].apply(null, args) + } + } finally { + if (cbs.length > len) { + // added more in the interim. + // de-zalgo, just in case, but don't call again. + cbs.splice(0, len) + process.nextTick(function () { + RES.apply(null, args) + }) + } else { + delete reqs[key] + } + } + }) +} + +function slice (args) { + var length = args.length + var array = [] + + for (var i = 0; i < length; i++) array[i] = args[i] + return array +} diff --git a/node_modules/inflight/package.json b/node_modules/inflight/package.json new file mode 100644 index 0000000..6084d35 --- /dev/null +++ b/node_modules/inflight/package.json @@ -0,0 +1,29 @@ +{ + "name": "inflight", + "version": "1.0.6", + "description": "Add callbacks to requests in flight to avoid async duplication", + "main": "inflight.js", + "files": [ + "inflight.js" + ], + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + }, + "devDependencies": { + "tap": "^7.1.2" + }, + "scripts": { + "test": "tap test.js --100" + }, + "repository": { + "type": "git", + "url": "https://github.com/npm/inflight.git" + }, + "author": "Isaac Z. Schlueter (http://blog.izs.me/)", + "bugs": { + "url": "https://github.com/isaacs/inflight/issues" + }, + "homepage": "https://github.com/isaacs/inflight", + "license": "ISC" +} diff --git a/node_modules/inherits/LICENSE b/node_modules/inherits/LICENSE new file mode 100644 index 0000000..dea3013 --- /dev/null +++ b/node_modules/inherits/LICENSE @@ -0,0 +1,16 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/node_modules/inherits/README.md b/node_modules/inherits/README.md new file mode 100644 index 0000000..b1c5665 --- /dev/null +++ b/node_modules/inherits/README.md @@ -0,0 +1,42 @@ +Browser-friendly inheritance fully compatible with standard node.js +[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor). + +This package exports standard `inherits` from node.js `util` module in +node environment, but also provides alternative browser-friendly +implementation through [browser +field](https://gist.github.com/shtylman/4339901). Alternative +implementation is a literal copy of standard one located in standalone +module to avoid requiring of `util`. It also has a shim for old +browsers with no `Object.create` support. + +While keeping you sure you are using standard `inherits` +implementation in node.js environment, it allows bundlers such as +[browserify](https://github.com/substack/node-browserify) to not +include full `util` package to your client code if all you need is +just `inherits` function. It worth, because browser shim for `util` +package is large and `inherits` is often the single function you need +from it. + +It's recommended to use this package instead of +`require('util').inherits` for any code that has chances to be used +not only in node.js but in browser too. + +## usage + +```js +var inherits = require('inherits'); +// then use exactly as the standard one +``` + +## note on version ~1.0 + +Version ~1.0 had completely different motivation and is not compatible +neither with 2.0 nor with standard node.js `inherits`. + +If you are using version ~1.0 and planning to switch to ~2.0, be +careful: + +* new version uses `super_` instead of `super` for referencing + superclass +* new version overwrites current prototype while old one preserves any + existing fields on it diff --git a/node_modules/inherits/inherits.js b/node_modules/inherits/inherits.js new file mode 100644 index 0000000..f71f2d9 --- /dev/null +++ b/node_modules/inherits/inherits.js @@ -0,0 +1,9 @@ +try { + var util = require('util'); + /* istanbul ignore next */ + if (typeof util.inherits !== 'function') throw ''; + module.exports = util.inherits; +} catch (e) { + /* istanbul ignore next */ + module.exports = require('./inherits_browser.js'); +} diff --git a/node_modules/inherits/inherits_browser.js b/node_modules/inherits/inherits_browser.js new file mode 100644 index 0000000..86bbb3d --- /dev/null +++ b/node_modules/inherits/inherits_browser.js @@ -0,0 +1,27 @@ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }) + } + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } +} diff --git a/node_modules/inherits/package.json b/node_modules/inherits/package.json new file mode 100644 index 0000000..37b4366 --- /dev/null +++ b/node_modules/inherits/package.json @@ -0,0 +1,29 @@ +{ + "name": "inherits", + "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", + "version": "2.0.4", + "keywords": [ + "inheritance", + "class", + "klass", + "oop", + "object-oriented", + "inherits", + "browser", + "browserify" + ], + "main": "./inherits.js", + "browser": "./inherits_browser.js", + "repository": "git://github.com/isaacs/inherits", + "license": "ISC", + "scripts": { + "test": "tap" + }, + "devDependencies": { + "tap": "^14.2.4" + }, + "files": [ + "inherits.js", + "inherits_browser.js" + ] +} diff --git a/node_modules/isexe/.npmignore b/node_modules/isexe/.npmignore new file mode 100644 index 0000000..c1cb757 --- /dev/null +++ b/node_modules/isexe/.npmignore @@ -0,0 +1,2 @@ +.nyc_output/ +coverage/ diff --git a/node_modules/isexe/LICENSE b/node_modules/isexe/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/isexe/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/isexe/README.md b/node_modules/isexe/README.md new file mode 100644 index 0000000..35769e8 --- /dev/null +++ b/node_modules/isexe/README.md @@ -0,0 +1,51 @@ +# isexe + +Minimal module to check if a file is executable, and a normal file. + +Uses `fs.stat` and tests against the `PATHEXT` environment variable on +Windows. + +## USAGE + +```javascript +var isexe = require('isexe') +isexe('some-file-name', function (err, isExe) { + if (err) { + console.error('probably file does not exist or something', err) + } else if (isExe) { + console.error('this thing can be run') + } else { + console.error('cannot be run') + } +}) + +// same thing but synchronous, throws errors +var isExe = isexe.sync('some-file-name') + +// treat errors as just "not executable" +isexe('maybe-missing-file', { ignoreErrors: true }, callback) +var isExe = isexe.sync('maybe-missing-file', { ignoreErrors: true }) +``` + +## API + +### `isexe(path, [options], [callback])` + +Check if the path is executable. If no callback provided, and a +global `Promise` object is available, then a Promise will be returned. + +Will raise whatever errors may be raised by `fs.stat`, unless +`options.ignoreErrors` is set to true. + +### `isexe.sync(path, [options])` + +Same as `isexe` but returns the value and throws any errors raised. + +### Options + +* `ignoreErrors` Treat all errors as "no, this is not executable", but + don't raise them. +* `uid` Number to use as the user id +* `gid` Number to use as the group id +* `pathExt` List of path extensions to use instead of `PATHEXT` + environment variable on Windows. diff --git a/node_modules/isexe/index.js b/node_modules/isexe/index.js new file mode 100644 index 0000000..553fb32 --- /dev/null +++ b/node_modules/isexe/index.js @@ -0,0 +1,57 @@ +var fs = require('fs') +var core +if (process.platform === 'win32' || global.TESTING_WINDOWS) { + core = require('./windows.js') +} else { + core = require('./mode.js') +} + +module.exports = isexe +isexe.sync = sync + +function isexe (path, options, cb) { + if (typeof options === 'function') { + cb = options + options = {} + } + + if (!cb) { + if (typeof Promise !== 'function') { + throw new TypeError('callback not provided') + } + + return new Promise(function (resolve, reject) { + isexe(path, options || {}, function (er, is) { + if (er) { + reject(er) + } else { + resolve(is) + } + }) + }) + } + + core(path, options || {}, function (er, is) { + // ignore EACCES because that just means we aren't allowed to run it + if (er) { + if (er.code === 'EACCES' || options && options.ignoreErrors) { + er = null + is = false + } + } + cb(er, is) + }) +} + +function sync (path, options) { + // my kingdom for a filtered catch + try { + return core.sync(path, options || {}) + } catch (er) { + if (options && options.ignoreErrors || er.code === 'EACCES') { + return false + } else { + throw er + } + } +} diff --git a/node_modules/isexe/mode.js b/node_modules/isexe/mode.js new file mode 100644 index 0000000..1995ea4 --- /dev/null +++ b/node_modules/isexe/mode.js @@ -0,0 +1,41 @@ +module.exports = isexe +isexe.sync = sync + +var fs = require('fs') + +function isexe (path, options, cb) { + fs.stat(path, function (er, stat) { + cb(er, er ? false : checkStat(stat, options)) + }) +} + +function sync (path, options) { + return checkStat(fs.statSync(path), options) +} + +function checkStat (stat, options) { + return stat.isFile() && checkMode(stat, options) +} + +function checkMode (stat, options) { + var mod = stat.mode + var uid = stat.uid + var gid = stat.gid + + var myUid = options.uid !== undefined ? + options.uid : process.getuid && process.getuid() + var myGid = options.gid !== undefined ? + options.gid : process.getgid && process.getgid() + + var u = parseInt('100', 8) + var g = parseInt('010', 8) + var o = parseInt('001', 8) + var ug = u | g + + var ret = (mod & o) || + (mod & g) && gid === myGid || + (mod & u) && uid === myUid || + (mod & ug) && myUid === 0 + + return ret +} diff --git a/node_modules/isexe/package.json b/node_modules/isexe/package.json new file mode 100644 index 0000000..e452689 --- /dev/null +++ b/node_modules/isexe/package.json @@ -0,0 +1,31 @@ +{ + "name": "isexe", + "version": "2.0.0", + "description": "Minimal module to check if a file is executable.", + "main": "index.js", + "directories": { + "test": "test" + }, + "devDependencies": { + "mkdirp": "^0.5.1", + "rimraf": "^2.5.0", + "tap": "^10.3.0" + }, + "scripts": { + "test": "tap test/*.js --100", + "preversion": "npm test", + "postversion": "npm publish", + "postpublish": "git push origin --all; git push origin --tags" + }, + "author": "Isaac Z. Schlueter (http://blog.izs.me/)", + "license": "ISC", + "repository": { + "type": "git", + "url": "git+https://github.com/isaacs/isexe.git" + }, + "keywords": [], + "bugs": { + "url": "https://github.com/isaacs/isexe/issues" + }, + "homepage": "https://github.com/isaacs/isexe#readme" +} diff --git a/node_modules/isexe/test/basic.js b/node_modules/isexe/test/basic.js new file mode 100644 index 0000000..d926df6 --- /dev/null +++ b/node_modules/isexe/test/basic.js @@ -0,0 +1,221 @@ +var t = require('tap') +var fs = require('fs') +var path = require('path') +var fixture = path.resolve(__dirname, 'fixtures') +var meow = fixture + '/meow.cat' +var mine = fixture + '/mine.cat' +var ours = fixture + '/ours.cat' +var fail = fixture + '/fail.false' +var noent = fixture + '/enoent.exe' +var mkdirp = require('mkdirp') +var rimraf = require('rimraf') + +var isWindows = process.platform === 'win32' +var hasAccess = typeof fs.access === 'function' +var winSkip = isWindows && 'windows' +var accessSkip = !hasAccess && 'no fs.access function' +var hasPromise = typeof Promise === 'function' +var promiseSkip = !hasPromise && 'no global Promise' + +function reset () { + delete require.cache[require.resolve('../')] + return require('../') +} + +t.test('setup fixtures', function (t) { + rimraf.sync(fixture) + mkdirp.sync(fixture) + fs.writeFileSync(meow, '#!/usr/bin/env cat\nmeow\n') + fs.chmodSync(meow, parseInt('0755', 8)) + fs.writeFileSync(fail, '#!/usr/bin/env false\n') + fs.chmodSync(fail, parseInt('0644', 8)) + fs.writeFileSync(mine, '#!/usr/bin/env cat\nmine\n') + fs.chmodSync(mine, parseInt('0744', 8)) + fs.writeFileSync(ours, '#!/usr/bin/env cat\nours\n') + fs.chmodSync(ours, parseInt('0754', 8)) + t.end() +}) + +t.test('promise', { skip: promiseSkip }, function (t) { + var isexe = reset() + t.test('meow async', function (t) { + isexe(meow).then(function (is) { + t.ok(is) + t.end() + }) + }) + t.test('fail async', function (t) { + isexe(fail).then(function (is) { + t.notOk(is) + t.end() + }) + }) + t.test('noent async', function (t) { + isexe(noent).catch(function (er) { + t.ok(er) + t.end() + }) + }) + t.test('noent ignore async', function (t) { + isexe(noent, { ignoreErrors: true }).then(function (is) { + t.notOk(is) + t.end() + }) + }) + t.end() +}) + +t.test('no promise', function (t) { + global.Promise = null + var isexe = reset() + t.throws('try to meow a promise', function () { + isexe(meow) + }) + t.end() +}) + +t.test('access', { skip: accessSkip || winSkip }, function (t) { + runTest(t) +}) + +t.test('mode', { skip: winSkip }, function (t) { + delete fs.access + delete fs.accessSync + var isexe = reset() + t.ok(isexe.sync(ours, { uid: 0, gid: 0 })) + t.ok(isexe.sync(mine, { uid: 0, gid: 0 })) + runTest(t) +}) + +t.test('windows', function (t) { + global.TESTING_WINDOWS = true + var pathExt = '.EXE;.CAT;.CMD;.COM' + t.test('pathExt option', function (t) { + runTest(t, { pathExt: '.EXE;.CAT;.CMD;.COM' }) + }) + t.test('pathExt env', function (t) { + process.env.PATHEXT = pathExt + runTest(t) + }) + t.test('no pathExt', function (t) { + // with a pathExt of '', any filename is fine. + // so the "fail" one would still pass. + runTest(t, { pathExt: '', skipFail: true }) + }) + t.test('pathext with empty entry', function (t) { + // with a pathExt of '', any filename is fine. + // so the "fail" one would still pass. + runTest(t, { pathExt: ';' + pathExt, skipFail: true }) + }) + t.end() +}) + +t.test('cleanup', function (t) { + rimraf.sync(fixture) + t.end() +}) + +function runTest (t, options) { + var isexe = reset() + + var optionsIgnore = Object.create(options || {}) + optionsIgnore.ignoreErrors = true + + if (!options || !options.skipFail) { + t.notOk(isexe.sync(fail, options)) + } + t.notOk(isexe.sync(noent, optionsIgnore)) + if (!options) { + t.ok(isexe.sync(meow)) + } else { + t.ok(isexe.sync(meow, options)) + } + + t.ok(isexe.sync(mine, options)) + t.ok(isexe.sync(ours, options)) + t.throws(function () { + isexe.sync(noent, options) + }) + + t.test('meow async', function (t) { + if (!options) { + isexe(meow, function (er, is) { + if (er) { + throw er + } + t.ok(is) + t.end() + }) + } else { + isexe(meow, options, function (er, is) { + if (er) { + throw er + } + t.ok(is) + t.end() + }) + } + }) + + t.test('mine async', function (t) { + isexe(mine, options, function (er, is) { + if (er) { + throw er + } + t.ok(is) + t.end() + }) + }) + + t.test('ours async', function (t) { + isexe(ours, options, function (er, is) { + if (er) { + throw er + } + t.ok(is) + t.end() + }) + }) + + if (!options || !options.skipFail) { + t.test('fail async', function (t) { + isexe(fail, options, function (er, is) { + if (er) { + throw er + } + t.notOk(is) + t.end() + }) + }) + } + + t.test('noent async', function (t) { + isexe(noent, options, function (er, is) { + t.ok(er) + t.notOk(is) + t.end() + }) + }) + + t.test('noent ignore async', function (t) { + isexe(noent, optionsIgnore, function (er, is) { + if (er) { + throw er + } + t.notOk(is) + t.end() + }) + }) + + t.test('directory is not executable', function (t) { + isexe(__dirname, options, function (er, is) { + if (er) { + throw er + } + t.notOk(is) + t.end() + }) + }) + + t.end() +} diff --git a/node_modules/isexe/windows.js b/node_modules/isexe/windows.js new file mode 100644 index 0000000..3499673 --- /dev/null +++ b/node_modules/isexe/windows.js @@ -0,0 +1,42 @@ +module.exports = isexe +isexe.sync = sync + +var fs = require('fs') + +function checkPathExt (path, options) { + var pathext = options.pathExt !== undefined ? + options.pathExt : process.env.PATHEXT + + if (!pathext) { + return true + } + + pathext = pathext.split(';') + if (pathext.indexOf('') !== -1) { + return true + } + for (var i = 0; i < pathext.length; i++) { + var p = pathext[i].toLowerCase() + if (p && path.substr(-p.length).toLowerCase() === p) { + return true + } + } + return false +} + +function checkStat (stat, path, options) { + if (!stat.isSymbolicLink() && !stat.isFile()) { + return false + } + return checkPathExt(path, options) +} + +function isexe (path, options, cb) { + fs.stat(path, function (er, stat) { + cb(er, er ? false : checkStat(stat, path, options)) + }) +} + +function sync (path, options) { + return checkStat(fs.statSync(path), path, options) +} diff --git a/node_modules/jsonfile/CHANGELOG.md b/node_modules/jsonfile/CHANGELOG.md new file mode 100644 index 0000000..7718857 --- /dev/null +++ b/node_modules/jsonfile/CHANGELOG.md @@ -0,0 +1,145 @@ +4.0.0 / 2017-07-12 +------------------ + +- **BREAKING:** Remove global `spaces` option. +- **BREAKING:** Drop support for Node 0.10, 0.12, and io.js. +- Remove undocumented `passParsingErrors` option. +- Added `EOL` override option to `writeFile` when using `spaces`. [#89] + +3.0.1 / 2017-07-05 +------------------ + +- Fixed bug in `writeFile` when there was a serialization error & no callback was passed. In previous versions, an empty file would be written; now no file is written. + +3.0.0 / 2017-04-25 +------------------ + +- Changed behavior of `throws` option for `readFileSync`; now does not throw filesystem errors when `throws` is `false` + +2.4.0 / 2016-09-15 +------------------ +### Changed +- added optional support for `graceful-fs` [#62] + +2.3.1 / 2016-05-13 +------------------ +- fix to support BOM. [#45][#45] + +2.3.0 / 2016-04-16 +------------------ +- add `throws` to `readFile()`. See [#39][#39] +- add support for any arbitrary `fs` module. Useful with [mock-fs](https://www.npmjs.com/package/mock-fs) + +2.2.3 / 2015-10-14 +------------------ +- include file name in parse error. See: https://github.com/jprichardson/node-jsonfile/pull/34 + +2.2.2 / 2015-09-16 +------------------ +- split out tests into separate files +- fixed `throws` when set to `true` in `readFileSync()`. See: https://github.com/jprichardson/node-jsonfile/pull/33 + +2.2.1 / 2015-06-25 +------------------ +- fixed regression when passing in string as encoding for options in `writeFile()` and `writeFileSync()`. See: https://github.com/jprichardson/node-jsonfile/issues/28 + +2.2.0 / 2015-06-25 +------------------ +- added `options.spaces` to `writeFile()` and `writeFileSync()` + +2.1.2 / 2015-06-22 +------------------ +- fixed if passed `readFileSync(file, 'utf8')`. See: https://github.com/jprichardson/node-jsonfile/issues/25 + +2.1.1 / 2015-06-19 +------------------ +- fixed regressions if `null` is passed for options. See: https://github.com/jprichardson/node-jsonfile/issues/24 + +2.1.0 / 2015-06-19 +------------------ +- cleanup: JavaScript Standard Style, rename files, dropped terst for assert +- methods now support JSON revivers/replacers + +2.0.1 / 2015-05-24 +------------------ +- update license attribute https://github.com/jprichardson/node-jsonfile/pull/21 + +2.0.0 / 2014-07-28 +------------------ +* added `\n` to end of file on write. [#14](https://github.com/jprichardson/node-jsonfile/pull/14) +* added `options.throws` to `readFileSync()` +* dropped support for Node v0.8 + +1.2.0 / 2014-06-29 +------------------ +* removed semicolons +* bugfix: passed `options` to `fs.readFile` and `fs.readFileSync`. This technically changes behavior, but +changes it according to docs. [#12][#12] + +1.1.1 / 2013-11-11 +------------------ +* fixed catching of callback bug (ffissore / #5) + +1.1.0 / 2013-10-11 +------------------ +* added `options` param to methods, (seanodell / #4) + +1.0.1 / 2013-09-05 +------------------ +* removed `homepage` field from package.json to remove NPM warning + +1.0.0 / 2013-06-28 +------------------ +* added `.npmignore`, #1 +* changed spacing default from `4` to `2` to follow Node conventions + +0.0.1 / 2012-09-10 +------------------ +* Initial release. + +[#89]: https://github.com/jprichardson/node-jsonfile/pull/89 +[#45]: https://github.com/jprichardson/node-jsonfile/issues/45 "Reading of UTF8-encoded (w/ BOM) files fails" +[#44]: https://github.com/jprichardson/node-jsonfile/issues/44 "Extra characters in written file" +[#43]: https://github.com/jprichardson/node-jsonfile/issues/43 "Prettyfy json when written to file" +[#42]: https://github.com/jprichardson/node-jsonfile/pull/42 "Moved fs.readFileSync within the try/catch" +[#41]: https://github.com/jprichardson/node-jsonfile/issues/41 "Linux: Hidden file not working" +[#40]: https://github.com/jprichardson/node-jsonfile/issues/40 "autocreate folder doesn't work from Path-value" +[#39]: https://github.com/jprichardson/node-jsonfile/pull/39 "Add `throws` option for readFile (async)" +[#38]: https://github.com/jprichardson/node-jsonfile/pull/38 "Update README.md writeFile[Sync] signature" +[#37]: https://github.com/jprichardson/node-jsonfile/pull/37 "support append file" +[#36]: https://github.com/jprichardson/node-jsonfile/pull/36 "Add typescript definition file." +[#35]: https://github.com/jprichardson/node-jsonfile/pull/35 "Add typescript definition file." +[#34]: https://github.com/jprichardson/node-jsonfile/pull/34 "readFile JSON parse error includes filename" +[#33]: https://github.com/jprichardson/node-jsonfile/pull/33 "fix throw->throws typo in readFileSync()" +[#32]: https://github.com/jprichardson/node-jsonfile/issues/32 "readFile & readFileSync can possible have strip-comments as an option?" +[#31]: https://github.com/jprichardson/node-jsonfile/pull/31 "[Modify] Support string include is unicode escape string" +[#30]: https://github.com/jprichardson/node-jsonfile/issues/30 "How to use Jsonfile package in Meteor.js App?" +[#29]: https://github.com/jprichardson/node-jsonfile/issues/29 "writefile callback if no error?" +[#28]: https://github.com/jprichardson/node-jsonfile/issues/28 "writeFile options argument broken " +[#27]: https://github.com/jprichardson/node-jsonfile/pull/27 "Use svg instead of png to get better image quality" +[#26]: https://github.com/jprichardson/node-jsonfile/issues/26 "Breaking change to fs-extra" +[#25]: https://github.com/jprichardson/node-jsonfile/issues/25 "support string encoding param for read methods" +[#24]: https://github.com/jprichardson/node-jsonfile/issues/24 "readFile: Passing in null options with a callback throws an error" +[#23]: https://github.com/jprichardson/node-jsonfile/pull/23 "Add appendFile and appendFileSync" +[#22]: https://github.com/jprichardson/node-jsonfile/issues/22 "Default value for spaces in readme.md is outdated" +[#21]: https://github.com/jprichardson/node-jsonfile/pull/21 "Update license attribute" +[#20]: https://github.com/jprichardson/node-jsonfile/issues/20 "Add simple caching functionallity" +[#19]: https://github.com/jprichardson/node-jsonfile/pull/19 "Add appendFileSync method" +[#18]: https://github.com/jprichardson/node-jsonfile/issues/18 "Add updateFile and updateFileSync methods" +[#17]: https://github.com/jprichardson/node-jsonfile/issues/17 "seem read & write sync has sequentially problem" +[#16]: https://github.com/jprichardson/node-jsonfile/pull/16 "export spaces defaulted to null" +[#15]: https://github.com/jprichardson/node-jsonfile/issues/15 "`jsonfile.spaces` should default to `null`" +[#14]: https://github.com/jprichardson/node-jsonfile/pull/14 "Add EOL at EOF" +[#13]: https://github.com/jprichardson/node-jsonfile/issues/13 "Add a final newline" +[#12]: https://github.com/jprichardson/node-jsonfile/issues/12 "readFile doesn't accept options" +[#11]: https://github.com/jprichardson/node-jsonfile/pull/11 "Added try,catch to readFileSync" +[#10]: https://github.com/jprichardson/node-jsonfile/issues/10 "No output or error from writeFile" +[#9]: https://github.com/jprichardson/node-jsonfile/pull/9 "Change 'js' to 'jf' in example." +[#8]: https://github.com/jprichardson/node-jsonfile/pull/8 "Updated forgotten module.exports to me." +[#7]: https://github.com/jprichardson/node-jsonfile/pull/7 "Add file name in error message" +[#6]: https://github.com/jprichardson/node-jsonfile/pull/6 "Use graceful-fs when possible" +[#5]: https://github.com/jprichardson/node-jsonfile/pull/5 "Jsonfile doesn't behave nicely when used inside a test suite." +[#4]: https://github.com/jprichardson/node-jsonfile/pull/4 "Added options parameter to writeFile and writeFileSync" +[#3]: https://github.com/jprichardson/node-jsonfile/issues/3 "test2" +[#2]: https://github.com/jprichardson/node-jsonfile/issues/2 "homepage field must be a string url. Deleted." +[#1]: https://github.com/jprichardson/node-jsonfile/pull/1 "adding an `.npmignore` file" diff --git a/node_modules/jsonfile/LICENSE b/node_modules/jsonfile/LICENSE new file mode 100644 index 0000000..cb7e807 --- /dev/null +++ b/node_modules/jsonfile/LICENSE @@ -0,0 +1,15 @@ +(The MIT License) + +Copyright (c) 2012-2015, JP Richardson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/jsonfile/README.md b/node_modules/jsonfile/README.md new file mode 100644 index 0000000..721685c --- /dev/null +++ b/node_modules/jsonfile/README.md @@ -0,0 +1,168 @@ +Node.js - jsonfile +================ + +Easily read/write JSON files. + +[![npm Package](https://img.shields.io/npm/v/jsonfile.svg?style=flat-square)](https://www.npmjs.org/package/jsonfile) +[![build status](https://secure.travis-ci.org/jprichardson/node-jsonfile.svg)](http://travis-ci.org/jprichardson/node-jsonfile) +[![windows Build status](https://img.shields.io/appveyor/ci/jprichardson/node-jsonfile/master.svg?label=windows%20build)](https://ci.appveyor.com/project/jprichardson/node-jsonfile/branch/master) + +Standard JavaScript + +Why? +---- + +Writing `JSON.stringify()` and then `fs.writeFile()` and `JSON.parse()` with `fs.readFile()` enclosed in `try/catch` blocks became annoying. + + + +Installation +------------ + + npm install --save jsonfile + + + +API +--- + +### readFile(filename, [options], callback) + +`options` (`object`, default `undefined`): Pass in any `fs.readFile` options or set `reviver` for a [JSON reviver](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse). + - `throws` (`boolean`, default: `true`). If `JSON.parse` throws an error, pass this error to the callback. + If `false`, returns `null` for the object. + + +```js +var jsonfile = require('jsonfile') +var file = '/tmp/data.json' +jsonfile.readFile(file, function(err, obj) { + console.dir(obj) +}) +``` + + +### readFileSync(filename, [options]) + +`options` (`object`, default `undefined`): Pass in any `fs.readFileSync` options or set `reviver` for a [JSON reviver](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse). +- `throws` (`boolean`, default: `true`). If an error is encountered reading or parsing the file, throw the error. If `false`, returns `null` for the object. + +```js +var jsonfile = require('jsonfile') +var file = '/tmp/data.json' + +console.dir(jsonfile.readFileSync(file)) +``` + + +### writeFile(filename, obj, [options], callback) + +`options`: Pass in any `fs.writeFile` options or set `replacer` for a [JSON replacer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). Can also pass in `spaces` and override `EOL` string. + + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/data.json' +var obj = {name: 'JP'} + +jsonfile.writeFile(file, obj, function (err) { + console.error(err) +}) +``` + +**formatting with spaces:** + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/data.json' +var obj = {name: 'JP'} + +jsonfile.writeFile(file, obj, {spaces: 2}, function(err) { + console.error(err) +}) +``` + +**overriding EOL:** + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/data.json' +var obj = {name: 'JP'} + +jsonfile.writeFile(file, obj, {spaces: 2, EOL: '\r\n'}, function(err) { + console.error(err) +}) +``` + +**appending to an existing JSON file:** + +You can use `fs.writeFile` option `{flag: 'a'}` to achieve this. + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/mayAlreadyExistedData.json' +var obj = {name: 'JP'} + +jsonfile.writeFile(file, obj, {flag: 'a'}, function (err) { + console.error(err) +}) +``` + +### writeFileSync(filename, obj, [options]) + +`options`: Pass in any `fs.writeFileSync` options or set `replacer` for a [JSON replacer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). Can also pass in `spaces` and override `EOL` string. + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/data.json' +var obj = {name: 'JP'} + +jsonfile.writeFileSync(file, obj) +``` + +**formatting with spaces:** + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/data.json' +var obj = {name: 'JP'} + +jsonfile.writeFileSync(file, obj, {spaces: 2}) +``` + +**overriding EOL:** + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/data.json' +var obj = {name: 'JP'} + +jsonfile.writeFileSync(file, obj, {spaces: 2, EOL: '\r\n'}) +``` + +**appending to an existing JSON file:** + +You can use `fs.writeFileSync` option `{flag: 'a'}` to achieve this. + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/mayAlreadyExistedData.json' +var obj = {name: 'JP'} + +jsonfile.writeFileSync(file, obj, {flag: 'a'}) +``` + +License +------- + +(MIT License) + +Copyright 2012-2016, JP Richardson diff --git a/node_modules/jsonfile/index.js b/node_modules/jsonfile/index.js new file mode 100644 index 0000000..d1e5827 --- /dev/null +++ b/node_modules/jsonfile/index.js @@ -0,0 +1,134 @@ +var _fs +try { + _fs = require('graceful-fs') +} catch (_) { + _fs = require('fs') +} + +function readFile (file, options, callback) { + if (callback == null) { + callback = options + options = {} + } + + if (typeof options === 'string') { + options = {encoding: options} + } + + options = options || {} + var fs = options.fs || _fs + + var shouldThrow = true + if ('throws' in options) { + shouldThrow = options.throws + } + + fs.readFile(file, options, function (err, data) { + if (err) return callback(err) + + data = stripBom(data) + + var obj + try { + obj = JSON.parse(data, options ? options.reviver : null) + } catch (err2) { + if (shouldThrow) { + err2.message = file + ': ' + err2.message + return callback(err2) + } else { + return callback(null, null) + } + } + + callback(null, obj) + }) +} + +function readFileSync (file, options) { + options = options || {} + if (typeof options === 'string') { + options = {encoding: options} + } + + var fs = options.fs || _fs + + var shouldThrow = true + if ('throws' in options) { + shouldThrow = options.throws + } + + try { + var content = fs.readFileSync(file, options) + content = stripBom(content) + return JSON.parse(content, options.reviver) + } catch (err) { + if (shouldThrow) { + err.message = file + ': ' + err.message + throw err + } else { + return null + } + } +} + +function stringify (obj, options) { + var spaces + var EOL = '\n' + if (typeof options === 'object' && options !== null) { + if (options.spaces) { + spaces = options.spaces + } + if (options.EOL) { + EOL = options.EOL + } + } + + var str = JSON.stringify(obj, options ? options.replacer : null, spaces) + + return str.replace(/\n/g, EOL) + EOL +} + +function writeFile (file, obj, options, callback) { + if (callback == null) { + callback = options + options = {} + } + options = options || {} + var fs = options.fs || _fs + + var str = '' + try { + str = stringify(obj, options) + } catch (err) { + // Need to return whether a callback was passed or not + if (callback) callback(err, null) + return + } + + fs.writeFile(file, str, options, callback) +} + +function writeFileSync (file, obj, options) { + options = options || {} + var fs = options.fs || _fs + + var str = stringify(obj, options) + // not sure if fs.writeFileSync returns anything, but just in case + return fs.writeFileSync(file, str, options) +} + +function stripBom (content) { + // we do this because JSON.parse would convert it to a utf8 string if encoding wasn't specified + if (Buffer.isBuffer(content)) content = content.toString('utf8') + content = content.replace(/^\uFEFF/, '') + return content +} + +var jsonfile = { + readFile: readFile, + readFileSync: readFileSync, + writeFile: writeFile, + writeFileSync: writeFileSync +} + +module.exports = jsonfile diff --git a/node_modules/jsonfile/package.json b/node_modules/jsonfile/package.json new file mode 100644 index 0000000..29e783f --- /dev/null +++ b/node_modules/jsonfile/package.json @@ -0,0 +1,37 @@ +{ + "name": "jsonfile", + "version": "4.0.0", + "description": "Easily read/write JSON files.", + "repository": { + "type": "git", + "url": "git@github.com:jprichardson/node-jsonfile.git" + }, + "keywords": [ + "read", + "write", + "file", + "json", + "fs", + "fs-extra" + ], + "author": "JP Richardson ", + "license": "MIT", + "dependencies": {}, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + }, + "devDependencies": { + "mocha": "2.x", + "rimraf": "^2.4.0", + "standard": "^10.0.3" + }, + "main": "index.js", + "files": [ + "index.js" + ], + "scripts": { + "lint": "standard", + "test": "npm run lint && npm run unit", + "unit": "mocha" + } +} diff --git a/node_modules/minimatch/LICENSE b/node_modules/minimatch/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/minimatch/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/minimatch/README.md b/node_modules/minimatch/README.md new file mode 100644 index 0000000..33ede1d --- /dev/null +++ b/node_modules/minimatch/README.md @@ -0,0 +1,230 @@ +# minimatch + +A minimal matching utility. + +[![Build Status](https://travis-ci.org/isaacs/minimatch.svg?branch=master)](http://travis-ci.org/isaacs/minimatch) + + +This is the matching library used internally by npm. + +It works by converting glob expressions into JavaScript `RegExp` +objects. + +## Usage + +```javascript +var minimatch = require("minimatch") + +minimatch("bar.foo", "*.foo") // true! +minimatch("bar.foo", "*.bar") // false! +minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy! +``` + +## Features + +Supports these glob features: + +* Brace Expansion +* Extended glob matching +* "Globstar" `**` matching + +See: + +* `man sh` +* `man bash` +* `man 3 fnmatch` +* `man 5 gitignore` + +## Minimatch Class + +Create a minimatch object by instantiating the `minimatch.Minimatch` class. + +```javascript +var Minimatch = require("minimatch").Minimatch +var mm = new Minimatch(pattern, options) +``` + +### Properties + +* `pattern` The original pattern the minimatch object represents. +* `options` The options supplied to the constructor. +* `set` A 2-dimensional array of regexp or string expressions. + Each row in the + array corresponds to a brace-expanded pattern. Each item in the row + corresponds to a single path-part. For example, the pattern + `{a,b/c}/d` would expand to a set of patterns like: + + [ [ a, d ] + , [ b, c, d ] ] + + If a portion of the pattern doesn't have any "magic" in it + (that is, it's something like `"foo"` rather than `fo*o?`), then it + will be left as a string rather than converted to a regular + expression. + +* `regexp` Created by the `makeRe` method. A single regular expression + expressing the entire pattern. This is useful in cases where you wish + to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled. +* `negate` True if the pattern is negated. +* `comment` True if the pattern is a comment. +* `empty` True if the pattern is `""`. + +### Methods + +* `makeRe` Generate the `regexp` member if necessary, and return it. + Will return `false` if the pattern is invalid. +* `match(fname)` Return true if the filename matches the pattern, or + false otherwise. +* `matchOne(fileArray, patternArray, partial)` Take a `/`-split + filename, and match it against a single row in the `regExpSet`. This + method is mainly for internal use, but is exposed so that it can be + used by a glob-walker that needs to avoid excessive filesystem calls. + +All other methods are internal, and will be called as necessary. + +### minimatch(path, pattern, options) + +Main export. Tests a path against the pattern using the options. + +```javascript +var isJS = minimatch(file, "*.js", { matchBase: true }) +``` + +### minimatch.filter(pattern, options) + +Returns a function that tests its +supplied argument, suitable for use with `Array.filter`. Example: + +```javascript +var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true})) +``` + +### minimatch.match(list, pattern, options) + +Match against the list of +files, in the style of fnmatch or glob. If nothing is matched, and +options.nonull is set, then return a list containing the pattern itself. + +```javascript +var javascripts = minimatch.match(fileList, "*.js", {matchBase: true})) +``` + +### minimatch.makeRe(pattern, options) + +Make a regular expression object from the pattern. + +## Options + +All options are `false` by default. + +### debug + +Dump a ton of stuff to stderr. + +### nobrace + +Do not expand `{a,b}` and `{1..3}` brace sets. + +### noglobstar + +Disable `**` matching against multiple folder names. + +### dot + +Allow patterns to match filenames starting with a period, even if +the pattern does not explicitly have a period in that spot. + +Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot` +is set. + +### noext + +Disable "extglob" style patterns like `+(a|b)`. + +### nocase + +Perform a case-insensitive match. + +### nonull + +When a match is not found by `minimatch.match`, return a list containing +the pattern itself if this option is set. When not set, an empty list +is returned if there are no matches. + +### matchBase + +If set, then patterns without slashes will be matched +against the basename of the path if it contains slashes. For example, +`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`. + +### nocomment + +Suppress the behavior of treating `#` at the start of a pattern as a +comment. + +### nonegate + +Suppress the behavior of treating a leading `!` character as negation. + +### flipNegate + +Returns from negate expressions the same as if they were not negated. +(Ie, true on a hit, false on a miss.) + +### partial + +Compare a partial path to a pattern. As long as the parts of the path that +are present are not contradicted by the pattern, it will be treated as a +match. This is useful in applications where you're walking through a +folder structure, and don't yet have the full path, but want to ensure that +you do not walk down paths that can never be a match. + +For example, + +```js +minimatch('/a/b', '/a/*/c/d', { partial: true }) // true, might be /a/b/c/d +minimatch('/a/b', '/**/d', { partial: true }) // true, might be /a/b/.../d +minimatch('/x/y/z', '/a/**/z', { partial: true }) // false, because x !== a +``` + +### allowWindowsEscape + +Windows path separator `\` is by default converted to `/`, which +prohibits the usage of `\` as a escape character. This flag skips that +behavior and allows using the escape character. + +## Comparisons to other fnmatch/glob implementations + +While strict compliance with the existing standards is a worthwhile +goal, some discrepancies exist between minimatch and other +implementations, and are intentional. + +If the pattern starts with a `!` character, then it is negated. Set the +`nonegate` flag to suppress this behavior, and treat leading `!` +characters normally. This is perhaps relevant if you wish to start the +pattern with a negative extglob pattern like `!(a|B)`. Multiple `!` +characters at the start of a pattern will negate the pattern multiple +times. + +If a pattern starts with `#`, then it is treated as a comment, and +will not match anything. Use `\#` to match a literal `#` at the +start of a line, or set the `nocomment` flag to suppress this behavior. + +The double-star character `**` is supported by default, unless the +`noglobstar` flag is set. This is supported in the manner of bsdglob +and bash 4.1, where `**` only has special significance if it is the only +thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but +`a/**b` will not. + +If an escaped pattern has no matches, and the `nonull` flag is set, +then minimatch.match returns the pattern as-provided, rather than +interpreting the character escapes. For example, +`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than +`"*a?"`. This is akin to setting the `nullglob` option in bash, except +that it does not resolve escaped pattern characters. + +If brace expansion is not disabled, then it is performed before any +other interpretation of the glob pattern. Thus, a pattern like +`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded +**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are +checked for validity. Since those two are valid, matching proceeds. diff --git a/node_modules/minimatch/minimatch.js b/node_modules/minimatch/minimatch.js new file mode 100644 index 0000000..fda45ad --- /dev/null +++ b/node_modules/minimatch/minimatch.js @@ -0,0 +1,947 @@ +module.exports = minimatch +minimatch.Minimatch = Minimatch + +var path = (function () { try { return require('path') } catch (e) {}}()) || { + sep: '/' +} +minimatch.sep = path.sep + +var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} +var expand = require('brace-expansion') + +var plTypes = { + '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, + '?': { open: '(?:', close: ')?' }, + '+': { open: '(?:', close: ')+' }, + '*': { open: '(?:', close: ')*' }, + '@': { open: '(?:', close: ')' } +} + +// any single thing other than / +// don't need to escape / when using new RegExp() +var qmark = '[^/]' + +// * => any number of characters +var star = qmark + '*?' + +// ** when dots are allowed. Anything goes, except .. and . +// not (^ or / followed by one or two dots followed by $ or /), +// followed by anything, any number of times. +var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' + +// not a ^ or / followed by a dot, +// followed by anything, any number of times. +var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' + +// characters that need to be escaped in RegExp. +var reSpecials = charSet('().*{}+?[]^$\\!') + +// "abc" -> { a:true, b:true, c:true } +function charSet (s) { + return s.split('').reduce(function (set, c) { + set[c] = true + return set + }, {}) +} + +// normalizes slashes. +var slashSplit = /\/+/ + +minimatch.filter = filter +function filter (pattern, options) { + options = options || {} + return function (p, i, list) { + return minimatch(p, pattern, options) + } +} + +function ext (a, b) { + b = b || {} + var t = {} + Object.keys(a).forEach(function (k) { + t[k] = a[k] + }) + Object.keys(b).forEach(function (k) { + t[k] = b[k] + }) + return t +} + +minimatch.defaults = function (def) { + if (!def || typeof def !== 'object' || !Object.keys(def).length) { + return minimatch + } + + var orig = minimatch + + var m = function minimatch (p, pattern, options) { + return orig(p, pattern, ext(def, options)) + } + + m.Minimatch = function Minimatch (pattern, options) { + return new orig.Minimatch(pattern, ext(def, options)) + } + m.Minimatch.defaults = function defaults (options) { + return orig.defaults(ext(def, options)).Minimatch + } + + m.filter = function filter (pattern, options) { + return orig.filter(pattern, ext(def, options)) + } + + m.defaults = function defaults (options) { + return orig.defaults(ext(def, options)) + } + + m.makeRe = function makeRe (pattern, options) { + return orig.makeRe(pattern, ext(def, options)) + } + + m.braceExpand = function braceExpand (pattern, options) { + return orig.braceExpand(pattern, ext(def, options)) + } + + m.match = function (list, pattern, options) { + return orig.match(list, pattern, ext(def, options)) + } + + return m +} + +Minimatch.defaults = function (def) { + return minimatch.defaults(def).Minimatch +} + +function minimatch (p, pattern, options) { + assertValidPattern(pattern) + + if (!options) options = {} + + // shortcut: comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + return false + } + + return new Minimatch(pattern, options).match(p) +} + +function Minimatch (pattern, options) { + if (!(this instanceof Minimatch)) { + return new Minimatch(pattern, options) + } + + assertValidPattern(pattern) + + if (!options) options = {} + + pattern = pattern.trim() + + // windows support: need to use /, not \ + if (!options.allowWindowsEscape && path.sep !== '/') { + pattern = pattern.split(path.sep).join('/') + } + + this.options = options + this.set = [] + this.pattern = pattern + this.regexp = null + this.negate = false + this.comment = false + this.empty = false + this.partial = !!options.partial + + // make the set of regexps etc. + this.make() +} + +Minimatch.prototype.debug = function () {} + +Minimatch.prototype.make = make +function make () { + var pattern = this.pattern + var options = this.options + + // empty patterns and comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + this.comment = true + return + } + if (!pattern) { + this.empty = true + return + } + + // step 1: figure out negation, etc. + this.parseNegate() + + // step 2: expand braces + var set = this.globSet = this.braceExpand() + + if (options.debug) this.debug = function debug() { console.error.apply(console, arguments) } + + this.debug(this.pattern, set) + + // step 3: now we have a set, so turn each one into a series of path-portion + // matching patterns. + // These will be regexps, except in the case of "**", which is + // set to the GLOBSTAR object for globstar behavior, + // and will not contain any / characters + set = this.globParts = set.map(function (s) { + return s.split(slashSplit) + }) + + this.debug(this.pattern, set) + + // glob --> regexps + set = set.map(function (s, si, set) { + return s.map(this.parse, this) + }, this) + + this.debug(this.pattern, set) + + // filter out everything that didn't compile properly. + set = set.filter(function (s) { + return s.indexOf(false) === -1 + }) + + this.debug(this.pattern, set) + + this.set = set +} + +Minimatch.prototype.parseNegate = parseNegate +function parseNegate () { + var pattern = this.pattern + var negate = false + var options = this.options + var negateOffset = 0 + + if (options.nonegate) return + + for (var i = 0, l = pattern.length + ; i < l && pattern.charAt(i) === '!' + ; i++) { + negate = !negate + negateOffset++ + } + + if (negateOffset) this.pattern = pattern.substr(negateOffset) + this.negate = negate +} + +// Brace expansion: +// a{b,c}d -> abd acd +// a{b,}c -> abc ac +// a{0..3}d -> a0d a1d a2d a3d +// a{b,c{d,e}f}g -> abg acdfg acefg +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg +// +// Invalid sets are not expanded. +// a{2..}b -> a{2..}b +// a{b}c -> a{b}c +minimatch.braceExpand = function (pattern, options) { + return braceExpand(pattern, options) +} + +Minimatch.prototype.braceExpand = braceExpand + +function braceExpand (pattern, options) { + if (!options) { + if (this instanceof Minimatch) { + options = this.options + } else { + options = {} + } + } + + pattern = typeof pattern === 'undefined' + ? this.pattern : pattern + + assertValidPattern(pattern) + + // Thanks to Yeting Li for + // improving this regexp to avoid a ReDOS vulnerability. + if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { + // shortcut. no need to expand. + return [pattern] + } + + return expand(pattern) +} + +var MAX_PATTERN_LENGTH = 1024 * 64 +var assertValidPattern = function (pattern) { + if (typeof pattern !== 'string') { + throw new TypeError('invalid pattern') + } + + if (pattern.length > MAX_PATTERN_LENGTH) { + throw new TypeError('pattern is too long') + } +} + +// parse a component of the expanded set. +// At this point, no pattern may contain "/" in it +// so we're going to return a 2d array, where each entry is the full +// pattern, split on '/', and then turned into a regular expression. +// A regexp is made at the end which joins each array with an +// escaped /, and another full one which joins each regexp with |. +// +// Following the lead of Bash 4.1, note that "**" only has special meaning +// when it is the *only* thing in a path portion. Otherwise, any series +// of * is equivalent to a single *. Globstar behavior is enabled by +// default, and can be disabled by setting options.noglobstar. +Minimatch.prototype.parse = parse +var SUBPARSE = {} +function parse (pattern, isSub) { + assertValidPattern(pattern) + + var options = this.options + + // shortcuts + if (pattern === '**') { + if (!options.noglobstar) + return GLOBSTAR + else + pattern = '*' + } + if (pattern === '') return '' + + var re = '' + var hasMagic = !!options.nocase + var escaping = false + // ? => one single character + var patternListStack = [] + var negativeLists = [] + var stateChar + var inClass = false + var reClassStart = -1 + var classStart = -1 + // . and .. never match anything that doesn't start with ., + // even when options.dot is set. + var patternStart = pattern.charAt(0) === '.' ? '' // anything + // not (start or / followed by . or .. followed by / or end) + : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' + : '(?!\\.)' + var self = this + + function clearStateChar () { + if (stateChar) { + // we had some state-tracking character + // that wasn't consumed by this pass. + switch (stateChar) { + case '*': + re += star + hasMagic = true + break + case '?': + re += qmark + hasMagic = true + break + default: + re += '\\' + stateChar + break + } + self.debug('clearStateChar %j %j', stateChar, re) + stateChar = false + } + } + + for (var i = 0, len = pattern.length, c + ; (i < len) && (c = pattern.charAt(i)) + ; i++) { + this.debug('%s\t%s %s %j', pattern, i, re, c) + + // skip over any that are escaped. + if (escaping && reSpecials[c]) { + re += '\\' + c + escaping = false + continue + } + + switch (c) { + /* istanbul ignore next */ + case '/': { + // completely not allowed, even escaped. + // Should already be path-split by now. + return false + } + + case '\\': + clearStateChar() + escaping = true + continue + + // the various stateChar values + // for the "extglob" stuff. + case '?': + case '*': + case '+': + case '@': + case '!': + this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) + + // all of those are literals inside a class, except that + // the glob [!a] means [^a] in regexp + if (inClass) { + this.debug(' in class') + if (c === '!' && i === classStart + 1) c = '^' + re += c + continue + } + + // if we already have a stateChar, then it means + // that there was something like ** or +? in there. + // Handle the stateChar, then proceed with this one. + self.debug('call clearStateChar %j', stateChar) + clearStateChar() + stateChar = c + // if extglob is disabled, then +(asdf|foo) isn't a thing. + // just clear the statechar *now*, rather than even diving into + // the patternList stuff. + if (options.noext) clearStateChar() + continue + + case '(': + if (inClass) { + re += '(' + continue + } + + if (!stateChar) { + re += '\\(' + continue + } + + patternListStack.push({ + type: stateChar, + start: i - 1, + reStart: re.length, + open: plTypes[stateChar].open, + close: plTypes[stateChar].close + }) + // negation is (?:(?!js)[^/]*) + re += stateChar === '!' ? '(?:(?!(?:' : '(?:' + this.debug('plType %j %j', stateChar, re) + stateChar = false + continue + + case ')': + if (inClass || !patternListStack.length) { + re += '\\)' + continue + } + + clearStateChar() + hasMagic = true + var pl = patternListStack.pop() + // negation is (?:(?!js)[^/]*) + // The others are (?:) + re += pl.close + if (pl.type === '!') { + negativeLists.push(pl) + } + pl.reEnd = re.length + continue + + case '|': + if (inClass || !patternListStack.length || escaping) { + re += '\\|' + escaping = false + continue + } + + clearStateChar() + re += '|' + continue + + // these are mostly the same in regexp and glob + case '[': + // swallow any state-tracking char before the [ + clearStateChar() + + if (inClass) { + re += '\\' + c + continue + } + + inClass = true + classStart = i + reClassStart = re.length + re += c + continue + + case ']': + // a right bracket shall lose its special + // meaning and represent itself in + // a bracket expression if it occurs + // first in the list. -- POSIX.2 2.8.3.2 + if (i === classStart + 1 || !inClass) { + re += '\\' + c + escaping = false + continue + } + + // handle the case where we left a class open. + // "[z-a]" is valid, equivalent to "\[z-a\]" + // split where the last [ was, make sure we don't have + // an invalid re. if so, re-walk the contents of the + // would-be class to re-translate any characters that + // were passed through as-is + // TODO: It would probably be faster to determine this + // without a try/catch and a new RegExp, but it's tricky + // to do safely. For now, this is safe and works. + var cs = pattern.substring(classStart + 1, i) + try { + RegExp('[' + cs + ']') + } catch (er) { + // not a valid class! + var sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' + hasMagic = hasMagic || sp[1] + inClass = false + continue + } + + // finish up the class. + hasMagic = true + inClass = false + re += c + continue + + default: + // swallow any state char that wasn't consumed + clearStateChar() + + if (escaping) { + // no need + escaping = false + } else if (reSpecials[c] + && !(c === '^' && inClass)) { + re += '\\' + } + + re += c + + } // switch + } // for + + // handle the case where we left a class open. + // "[abc" is valid, equivalent to "\[abc" + if (inClass) { + // split where the last [ was, and escape it + // this is a huge pita. We now have to re-walk + // the contents of the would-be class to re-translate + // any characters that were passed through as-is + cs = pattern.substr(classStart + 1) + sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + hasMagic = hasMagic || sp[1] + } + + // handle the case where we had a +( thing at the *end* + // of the pattern. + // each pattern list stack adds 3 chars, and we need to go through + // and escape any | chars that were passed through as-is for the regexp. + // Go through and escape them, taking care not to double-escape any + // | chars that were already escaped. + for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { + var tail = re.slice(pl.reStart + pl.open.length) + this.debug('setting tail', re, pl) + // maybe some even number of \, then maybe 1 \, followed by a | + tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { + if (!$2) { + // the | isn't already escaped, so escape it. + $2 = '\\' + } + + // need to escape all those slashes *again*, without escaping the + // one that we need for escaping the | character. As it works out, + // escaping an even number of slashes can be done by simply repeating + // it exactly after itself. That's why this trick works. + // + // I am sorry that you have to see this. + return $1 + $1 + $2 + '|' + }) + + this.debug('tail=%j\n %s', tail, tail, pl, re) + var t = pl.type === '*' ? star + : pl.type === '?' ? qmark + : '\\' + pl.type + + hasMagic = true + re = re.slice(0, pl.reStart) + t + '\\(' + tail + } + + // handle trailing things that only matter at the very end. + clearStateChar() + if (escaping) { + // trailing \\ + re += '\\\\' + } + + // only need to apply the nodot start if the re starts with + // something that could conceivably capture a dot + var addPatternStart = false + switch (re.charAt(0)) { + case '[': case '.': case '(': addPatternStart = true + } + + // Hack to work around lack of negative lookbehind in JS + // A pattern like: *.!(x).!(y|z) needs to ensure that a name + // like 'a.xyz.yz' doesn't match. So, the first negative + // lookahead, has to look ALL the way ahead, to the end of + // the pattern. + for (var n = negativeLists.length - 1; n > -1; n--) { + var nl = negativeLists[n] + + var nlBefore = re.slice(0, nl.reStart) + var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) + var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + var nlAfter = re.slice(nl.reEnd) + + nlLast += nlAfter + + // Handle nested stuff like *(*.js|!(*.json)), where open parens + // mean that we should *not* include the ) in the bit that is considered + // "after" the negated section. + var openParensBefore = nlBefore.split('(').length - 1 + var cleanAfter = nlAfter + for (i = 0; i < openParensBefore; i++) { + cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') + } + nlAfter = cleanAfter + + var dollar = '' + if (nlAfter === '' && isSub !== SUBPARSE) { + dollar = '$' + } + var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast + re = newRe + } + + // if the re is not "" at this point, then we need to make sure + // it doesn't match against an empty path part. + // Otherwise a/* will match a/, which it should not. + if (re !== '' && hasMagic) { + re = '(?=.)' + re + } + + if (addPatternStart) { + re = patternStart + re + } + + // parsing just a piece of a larger pattern. + if (isSub === SUBPARSE) { + return [re, hasMagic] + } + + // skip the regexp for non-magical patterns + // unescape anything in it, though, so that it'll be + // an exact match against a file etc. + if (!hasMagic) { + return globUnescape(pattern) + } + + var flags = options.nocase ? 'i' : '' + try { + var regExp = new RegExp('^' + re + '$', flags) + } catch (er) /* istanbul ignore next - should be impossible */ { + // If it was an invalid regular expression, then it can't match + // anything. This trick looks for a character after the end of + // the string, which is of course impossible, except in multi-line + // mode, but it's not a /m regex. + return new RegExp('$.') + } + + regExp._glob = pattern + regExp._src = re + + return regExp +} + +minimatch.makeRe = function (pattern, options) { + return new Minimatch(pattern, options || {}).makeRe() +} + +Minimatch.prototype.makeRe = makeRe +function makeRe () { + if (this.regexp || this.regexp === false) return this.regexp + + // at this point, this.set is a 2d array of partial + // pattern strings, or "**". + // + // It's better to use .match(). This function shouldn't + // be used, really, but it's pretty convenient sometimes, + // when you just want to work with a regex. + var set = this.set + + if (!set.length) { + this.regexp = false + return this.regexp + } + var options = this.options + + var twoStar = options.noglobstar ? star + : options.dot ? twoStarDot + : twoStarNoDot + var flags = options.nocase ? 'i' : '' + + var re = set.map(function (pattern) { + return pattern.map(function (p) { + return (p === GLOBSTAR) ? twoStar + : (typeof p === 'string') ? regExpEscape(p) + : p._src + }).join('\\\/') + }).join('|') + + // must match entire pattern + // ending in a * or ** will make it less strict. + re = '^(?:' + re + ')$' + + // can match anything, as long as it's not this. + if (this.negate) re = '^(?!' + re + ').*$' + + try { + this.regexp = new RegExp(re, flags) + } catch (ex) /* istanbul ignore next - should be impossible */ { + this.regexp = false + } + return this.regexp +} + +minimatch.match = function (list, pattern, options) { + options = options || {} + var mm = new Minimatch(pattern, options) + list = list.filter(function (f) { + return mm.match(f) + }) + if (mm.options.nonull && !list.length) { + list.push(pattern) + } + return list +} + +Minimatch.prototype.match = function match (f, partial) { + if (typeof partial === 'undefined') partial = this.partial + this.debug('match', f, this.pattern) + // short-circuit in the case of busted things. + // comments, etc. + if (this.comment) return false + if (this.empty) return f === '' + + if (f === '/' && partial) return true + + var options = this.options + + // windows: need to use /, not \ + if (path.sep !== '/') { + f = f.split(path.sep).join('/') + } + + // treat the test path as a set of pathparts. + f = f.split(slashSplit) + this.debug(this.pattern, 'split', f) + + // just ONE of the pattern sets in this.set needs to match + // in order for it to be valid. If negating, then just one + // match means that we have failed. + // Either way, return on the first hit. + + var set = this.set + this.debug(this.pattern, 'set', set) + + // Find the basename of the path by looking for the last non-empty segment + var filename + var i + for (i = f.length - 1; i >= 0; i--) { + filename = f[i] + if (filename) break + } + + for (i = 0; i < set.length; i++) { + var pattern = set[i] + var file = f + if (options.matchBase && pattern.length === 1) { + file = [filename] + } + var hit = this.matchOne(file, pattern, partial) + if (hit) { + if (options.flipNegate) return true + return !this.negate + } + } + + // didn't get any hits. this is success if it's a negative + // pattern, failure otherwise. + if (options.flipNegate) return false + return this.negate +} + +// set partial to true to test if, for example, +// "/a/b" matches the start of "/*/b/*/d" +// Partial means, if you run out of file before you run +// out of pattern, then that's fine, as long as all +// the parts match. +Minimatch.prototype.matchOne = function (file, pattern, partial) { + var options = this.options + + this.debug('matchOne', + { 'this': this, file: file, pattern: pattern }) + + this.debug('matchOne', file.length, pattern.length) + + for (var fi = 0, + pi = 0, + fl = file.length, + pl = pattern.length + ; (fi < fl) && (pi < pl) + ; fi++, pi++) { + this.debug('matchOne loop') + var p = pattern[pi] + var f = file[fi] + + this.debug(pattern, p, f) + + // should be impossible. + // some invalid regexp stuff in the set. + /* istanbul ignore if */ + if (p === false) return false + + if (p === GLOBSTAR) { + this.debug('GLOBSTAR', [pattern, p, f]) + + // "**" + // a/**/b/**/c would match the following: + // a/b/x/y/z/c + // a/x/y/z/b/c + // a/b/x/b/x/c + // a/b/c + // To do this, take the rest of the pattern after + // the **, and see if it would match the file remainder. + // If so, return success. + // If not, the ** "swallows" a segment, and try again. + // This is recursively awful. + // + // a/**/b/**/c matching a/b/x/y/z/c + // - a matches a + // - doublestar + // - matchOne(b/x/y/z/c, b/**/c) + // - b matches b + // - doublestar + // - matchOne(x/y/z/c, c) -> no + // - matchOne(y/z/c, c) -> no + // - matchOne(z/c, c) -> no + // - matchOne(c, c) yes, hit + var fr = fi + var pr = pi + 1 + if (pr === pl) { + this.debug('** at the end') + // a ** at the end will just swallow the rest. + // We have found a match. + // however, it will not swallow /.x, unless + // options.dot is set. + // . and .. are *never* matched by **, for explosively + // exponential reasons. + for (; fi < fl; fi++) { + if (file[fi] === '.' || file[fi] === '..' || + (!options.dot && file[fi].charAt(0) === '.')) return false + } + return true + } + + // ok, let's see if we can swallow whatever we can. + while (fr < fl) { + var swallowee = file[fr] + + this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) + + // XXX remove this slice. Just pass the start index. + if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { + this.debug('globstar found match!', fr, fl, swallowee) + // found a match. + return true + } else { + // can't swallow "." or ".." ever. + // can only swallow ".foo" when explicitly asked. + if (swallowee === '.' || swallowee === '..' || + (!options.dot && swallowee.charAt(0) === '.')) { + this.debug('dot detected!', file, fr, pattern, pr) + break + } + + // ** swallows a segment, and continue. + this.debug('globstar swallow a segment, and continue') + fr++ + } + } + + // no match was found. + // However, in partial mode, we can't say this is necessarily over. + // If there's more *pattern* left, then + /* istanbul ignore if */ + if (partial) { + // ran out of file + this.debug('\n>>> no match, partial?', file, fr, pattern, pr) + if (fr === fl) return true + } + return false + } + + // something other than ** + // non-magic patterns just have to match exactly + // patterns with magic have been turned into regexps. + var hit + if (typeof p === 'string') { + hit = f === p + this.debug('string match', p, f, hit) + } else { + hit = f.match(p) + this.debug('pattern match', p, f, hit) + } + + if (!hit) return false + } + + // Note: ending in / means that we'll get a final "" + // at the end of the pattern. This can only match a + // corresponding "" at the end of the file. + // If the file ends in /, then it can only match a + // a pattern that ends in /, unless the pattern just + // doesn't have any more for it. But, a/b/ should *not* + // match "a/b/*", even though "" matches against the + // [^/]*? pattern, except in partial mode, where it might + // simply not be reached yet. + // However, a/b/ should still satisfy a/* + + // now either we fell off the end of the pattern, or we're done. + if (fi === fl && pi === pl) { + // ran out of pattern and filename at the same time. + // an exact hit! + return true + } else if (fi === fl) { + // ran out of file, but still had pattern left. + // this is ok if we're doing the match as part of + // a glob fs traversal. + return partial + } else /* istanbul ignore else */ if (pi === pl) { + // ran out of pattern, still have file left. + // this is only acceptable if we're on the very last + // empty segment of a file with a trailing slash. + // a/* should match a/b/ + return (fi === fl - 1) && (file[fi] === '') + } + + // should be unreachable. + /* istanbul ignore next */ + throw new Error('wtf?') +} + +// replace stuff like \* with * +function globUnescape (s) { + return s.replace(/\\(.)/g, '$1') +} + +function regExpEscape (s) { + return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') +} diff --git a/node_modules/minimatch/package.json b/node_modules/minimatch/package.json new file mode 100644 index 0000000..566efdf --- /dev/null +++ b/node_modules/minimatch/package.json @@ -0,0 +1,33 @@ +{ + "author": "Isaac Z. Schlueter (http://blog.izs.me)", + "name": "minimatch", + "description": "a glob matcher in javascript", + "version": "3.1.2", + "publishConfig": { + "tag": "v3-legacy" + }, + "repository": { + "type": "git", + "url": "git://github.com/isaacs/minimatch.git" + }, + "main": "minimatch.js", + "scripts": { + "test": "tap", + "preversion": "npm test", + "postversion": "npm publish", + "postpublish": "git push origin --all; git push origin --tags" + }, + "engines": { + "node": "*" + }, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "devDependencies": { + "tap": "^15.1.6" + }, + "license": "ISC", + "files": [ + "minimatch.js" + ] +} diff --git a/node_modules/nice-try/CHANGELOG.md b/node_modules/nice-try/CHANGELOG.md new file mode 100644 index 0000000..9e6baf2 --- /dev/null +++ b/node_modules/nice-try/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [1.0.5] - 2018-08-25 + +### Changed + +- Removed `prepublish` script from `package.json` + +## [1.0.4] - 2017-08-08 + +### New + +- Added a changelog + +### Changed + +- Ignore `yarn.lock` and `package-lock.json` files \ No newline at end of file diff --git a/node_modules/nice-try/LICENSE b/node_modules/nice-try/LICENSE new file mode 100644 index 0000000..681c8f5 --- /dev/null +++ b/node_modules/nice-try/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Tobias Reich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/nice-try/README.md b/node_modules/nice-try/README.md new file mode 100644 index 0000000..5b83b78 --- /dev/null +++ b/node_modules/nice-try/README.md @@ -0,0 +1,32 @@ +# nice-try + +[![Travis Build Status](https://travis-ci.org/electerious/nice-try.svg?branch=master)](https://travis-ci.org/electerious/nice-try) [![AppVeyor Status](https://ci.appveyor.com/api/projects/status/8tqb09wrwci3xf8l?svg=true)](https://ci.appveyor.com/project/electerious/nice-try) [![Coverage Status](https://coveralls.io/repos/github/electerious/nice-try/badge.svg?branch=master)](https://coveralls.io/github/electerious/nice-try?branch=master) [![Dependencies](https://david-dm.org/electerious/nice-try.svg)](https://david-dm.org/electerious/nice-try#info=dependencies) [![Greenkeeper badge](https://badges.greenkeeper.io/electerious/nice-try.svg)](https://greenkeeper.io/) + +A function that tries to execute a function and discards any error that occurs. + +## Install + +``` +npm install nice-try +``` + +## Usage + +```js +const niceTry = require('nice-try') + +niceTry(() => JSON.parse('true')) // true +niceTry(() => JSON.parse('truee')) // undefined +niceTry() // undefined +niceTry(true) // undefined +``` + +## API + +### Parameters + +- `fn` `{Function}` Function that might or might not throw an error. + +### Returns + +- `{?*}` Return-value of the function when no error occurred. \ No newline at end of file diff --git a/node_modules/nice-try/package.json b/node_modules/nice-try/package.json new file mode 100644 index 0000000..d3d8887 --- /dev/null +++ b/node_modules/nice-try/package.json @@ -0,0 +1,33 @@ +{ + "name": "nice-try", + "version": "1.0.5", + "authors": [ + "Tobias Reich " + ], + "description": "Tries to execute a function and discards any error that occurs", + "main": "src/index.js", + "keywords": [ + "try", + "catch", + "error" + ], + "license": "MIT", + "homepage": "https://github.com/electerious/nice-try", + "repository": { + "type": "git", + "url": "https://github.com/electerious/nice-try.git" + }, + "files": [ + "src" + ], + "scripts": { + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "test": "nyc node_modules/mocha/bin/_mocha" + }, + "devDependencies": { + "chai": "^4.1.2", + "coveralls": "^3.0.0", + "nyc": "^12.0.1", + "mocha": "^5.1.1" + } +} diff --git a/node_modules/nice-try/src/index.js b/node_modules/nice-try/src/index.js new file mode 100644 index 0000000..837506f --- /dev/null +++ b/node_modules/nice-try/src/index.js @@ -0,0 +1,12 @@ +'use strict' + +/** + * Tries to execute a function and discards any error that occurs. + * @param {Function} fn - Function that might or might not throw an error. + * @returns {?*} Return-value of the function when no error occurred. + */ +module.exports = function(fn) { + + try { return fn() } catch (e) {} + +} \ No newline at end of file diff --git a/node_modules/objectorarray/LICENSE b/node_modules/objectorarray/LICENSE new file mode 100644 index 0000000..8333b51 --- /dev/null +++ b/node_modules/objectorarray/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Zhou Hancheng (z308114274@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/objectorarray/README.md b/node_modules/objectorarray/README.md new file mode 100644 index 0000000..ceffd2b --- /dev/null +++ b/node_modules/objectorarray/README.md @@ -0,0 +1,50 @@ +# objectorarray + [![npm version][1]][0] [![downloads][2]][0] + +Is the value an object or an array but not null or RegExp? + +## Install + +``` + $ yarn add objectorarray +``` + +## Usage + +```js + import objectorarray from 'objectorarray' +``` + +### True + +All of the following return true: + +```js + objectorarray({}) + objectorarray([]) + objectorarray(Object.create({})) + objectorarray(Object.create(Object.prototype)) + objectorarray(Object.create(null)) + objectorarray(new Foo) +``` + +### False + +All of the following return false: + +```js + objectorarray() + objectorarray(function () {}) + objectorarray(1) + objectorarray(/foo/) + objectorarray(undefined) + objectorarray(null) +``` + +## License + +MIT + +[0]: https://npmjs.org/package/objectorarray +[1]: https://img.shields.io/npm/v/objectorarray.svg?style=flat-square +[2]: http://img.shields.io/npm/dm/objectorarray.svg?style=flat-square diff --git a/node_modules/objectorarray/index.js b/node_modules/objectorarray/index.js new file mode 100644 index 0000000..7035618 --- /dev/null +++ b/node_modules/objectorarray/index.js @@ -0,0 +1,3 @@ +module.exports = (val) => { + return val != null && typeof val === 'object' && val.constructor !== RegExp +} diff --git a/node_modules/objectorarray/package.json b/node_modules/objectorarray/package.json new file mode 100644 index 0000000..a854ab5 --- /dev/null +++ b/node_modules/objectorarray/package.json @@ -0,0 +1,27 @@ +{ + "name": "objectorarray", + "version": "1.0.5", + "description": "Is the value an object or an array but not null?", + "scripts": { + "test": "tape test.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ZhouHansen/objectnotnull.git" + }, + "keywords": [ + "javascript" + ], + "author": { + "name": "zhouhancheng", + "email": "z308114274@gmail.com" + }, + "license": "ISC", + "bugs": { + "url": "https://github.com/ZhouHansen/objectnotnull/issues" + }, + "homepage": "https://github.com/ZhouHansen/objectnotnull#readme", + "devDependencies": { + "tape": "^4.8.0" + } +} diff --git a/node_modules/objectorarray/test.js b/node_modules/objectorarray/test.js new file mode 100644 index 0000000..a22e4e6 --- /dev/null +++ b/node_modules/objectorarray/test.js @@ -0,0 +1,23 @@ +var test = require('tape') +var objectorarray = require('./') + +test('objectorarray', t => { + t.plan(12) + + t.ok(objectorarray({})) + t.ok(objectorarray([])) + t.ok(objectorarray(Object.create({}))) + t.ok(objectorarray(Object.create(Object.prototype))) + t.ok(objectorarray(Object.create(null))) + t.ok(objectorarray(new Foo)) + function Foo (bar) { + this.bar = bar + } + + t.notOk(objectorarray()) + t.notOk(objectorarray(function () {})) + t.notOk(objectorarray(1)) + t.notOk(objectorarray(/foo/)) + t.notOk(objectorarray(null)) + t.notOk(objectorarray(undefined)) +}) diff --git a/node_modules/once/LICENSE b/node_modules/once/LICENSE new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/node_modules/once/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/once/README.md b/node_modules/once/README.md new file mode 100644 index 0000000..1f1ffca --- /dev/null +++ b/node_modules/once/README.md @@ -0,0 +1,79 @@ +# once + +Only call a function once. + +## usage + +```javascript +var once = require('once') + +function load (file, cb) { + cb = once(cb) + loader.load('file') + loader.once('load', cb) + loader.once('error', cb) +} +``` + +Or add to the Function.prototype in a responsible way: + +```javascript +// only has to be done once +require('once').proto() + +function load (file, cb) { + cb = cb.once() + loader.load('file') + loader.once('load', cb) + loader.once('error', cb) +} +``` + +Ironically, the prototype feature makes this module twice as +complicated as necessary. + +To check whether you function has been called, use `fn.called`. Once the +function is called for the first time the return value of the original +function is saved in `fn.value` and subsequent calls will continue to +return this value. + +```javascript +var once = require('once') + +function load (cb) { + cb = once(cb) + var stream = createStream() + stream.once('data', cb) + stream.once('end', function () { + if (!cb.called) cb(new Error('not found')) + }) +} +``` + +## `once.strict(func)` + +Throw an error if the function is called twice. + +Some functions are expected to be called only once. Using `once` for them would +potentially hide logical errors. + +In the example below, the `greet` function has to call the callback only once: + +```javascript +function greet (name, cb) { + // return is missing from the if statement + // when no name is passed, the callback is called twice + if (!name) cb('Hello anonymous') + cb('Hello ' + name) +} + +function log (msg) { + console.log(msg) +} + +// this will print 'Hello anonymous' but the logical error will be missed +greet(null, once(msg)) + +// once.strict will print 'Hello anonymous' and throw an error when the callback will be called the second time +greet(null, once.strict(msg)) +``` diff --git a/node_modules/once/once.js b/node_modules/once/once.js new file mode 100644 index 0000000..2354067 --- /dev/null +++ b/node_modules/once/once.js @@ -0,0 +1,42 @@ +var wrappy = require('wrappy') +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} diff --git a/node_modules/once/package.json b/node_modules/once/package.json new file mode 100644 index 0000000..16815b2 --- /dev/null +++ b/node_modules/once/package.json @@ -0,0 +1,33 @@ +{ + "name": "once", + "version": "1.4.0", + "description": "Run a function exactly one time", + "main": "once.js", + "directories": { + "test": "test" + }, + "dependencies": { + "wrappy": "1" + }, + "devDependencies": { + "tap": "^7.0.1" + }, + "scripts": { + "test": "tap test/*.js" + }, + "files": [ + "once.js" + ], + "repository": { + "type": "git", + "url": "git://github.com/isaacs/once" + }, + "keywords": [ + "once", + "function", + "one", + "single" + ], + "author": "Isaac Z. Schlueter (http://blog.izs.me/)", + "license": "ISC" +} diff --git a/node_modules/path-is-absolute/index.js b/node_modules/path-is-absolute/index.js new file mode 100644 index 0000000..22aa6c3 --- /dev/null +++ b/node_modules/path-is-absolute/index.js @@ -0,0 +1,20 @@ +'use strict'; + +function posix(path) { + return path.charAt(0) === '/'; +} + +function win32(path) { + // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 + var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + var result = splitDeviceRe.exec(path); + var device = result[1] || ''; + var isUnc = Boolean(device && device.charAt(1) !== ':'); + + // UNC paths are always absolute + return Boolean(result[2] || isUnc); +} + +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; diff --git a/node_modules/path-is-absolute/license b/node_modules/path-is-absolute/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/node_modules/path-is-absolute/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/path-is-absolute/package.json b/node_modules/path-is-absolute/package.json new file mode 100644 index 0000000..91196d5 --- /dev/null +++ b/node_modules/path-is-absolute/package.json @@ -0,0 +1,43 @@ +{ + "name": "path-is-absolute", + "version": "1.0.1", + "description": "Node.js 0.12 path.isAbsolute() ponyfill", + "license": "MIT", + "repository": "sindresorhus/path-is-absolute", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "xo && node test.js" + }, + "files": [ + "index.js" + ], + "keywords": [ + "path", + "paths", + "file", + "dir", + "absolute", + "isabsolute", + "is-absolute", + "built-in", + "util", + "utils", + "core", + "ponyfill", + "polyfill", + "shim", + "is", + "detect", + "check" + ], + "devDependencies": { + "xo": "^0.16.0" + } +} diff --git a/node_modules/path-is-absolute/readme.md b/node_modules/path-is-absolute/readme.md new file mode 100644 index 0000000..8dbdf5f --- /dev/null +++ b/node_modules/path-is-absolute/readme.md @@ -0,0 +1,59 @@ +# path-is-absolute [![Build Status](https://travis-ci.org/sindresorhus/path-is-absolute.svg?branch=master)](https://travis-ci.org/sindresorhus/path-is-absolute) + +> Node.js 0.12 [`path.isAbsolute()`](http://nodejs.org/api/path.html#path_path_isabsolute_path) [ponyfill](https://ponyfill.com) + + +## Install + +``` +$ npm install --save path-is-absolute +``` + + +## Usage + +```js +const pathIsAbsolute = require('path-is-absolute'); + +// Running on Linux +pathIsAbsolute('/home/foo'); +//=> true +pathIsAbsolute('C:/Users/foo'); +//=> false + +// Running on Windows +pathIsAbsolute('C:/Users/foo'); +//=> true +pathIsAbsolute('/home/foo'); +//=> false + +// Running on any OS +pathIsAbsolute.posix('/home/foo'); +//=> true +pathIsAbsolute.posix('C:/Users/foo'); +//=> false +pathIsAbsolute.win32('C:/Users/foo'); +//=> true +pathIsAbsolute.win32('/home/foo'); +//=> false +``` + + +## API + +See the [`path.isAbsolute()` docs](http://nodejs.org/api/path.html#path_path_isabsolute_path). + +### pathIsAbsolute(path) + +### pathIsAbsolute.posix(path) + +POSIX specific version. + +### pathIsAbsolute.win32(path) + +Windows specific version. + + +## License + +MIT © [Sindre Sorhus](https://sindresorhus.com) diff --git a/node_modules/path-key/index.js b/node_modules/path-key/index.js new file mode 100644 index 0000000..62c8250 --- /dev/null +++ b/node_modules/path-key/index.js @@ -0,0 +1,13 @@ +'use strict'; +module.exports = opts => { + opts = opts || {}; + + const env = opts.env || process.env; + const platform = opts.platform || process.platform; + + if (platform !== 'win32') { + return 'PATH'; + } + + return Object.keys(env).find(x => x.toUpperCase() === 'PATH') || 'Path'; +}; diff --git a/node_modules/path-key/license b/node_modules/path-key/license new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/node_modules/path-key/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/path-key/package.json b/node_modules/path-key/package.json new file mode 100644 index 0000000..81e0e8b --- /dev/null +++ b/node_modules/path-key/package.json @@ -0,0 +1,39 @@ +{ + "name": "path-key", + "version": "2.0.1", + "description": "Get the PATH environment variable key cross-platform", + "license": "MIT", + "repository": "sindresorhus/path-key", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "engines": { + "node": ">=4" + }, + "scripts": { + "test": "xo && ava" + }, + "files": [ + "index.js" + ], + "keywords": [ + "path", + "key", + "environment", + "env", + "variable", + "var", + "get", + "cross-platform", + "windows" + ], + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "xo": { + "esnext": true + } +} diff --git a/node_modules/path-key/readme.md b/node_modules/path-key/readme.md new file mode 100644 index 0000000..cb5710a --- /dev/null +++ b/node_modules/path-key/readme.md @@ -0,0 +1,51 @@ +# path-key [![Build Status](https://travis-ci.org/sindresorhus/path-key.svg?branch=master)](https://travis-ci.org/sindresorhus/path-key) + +> Get the [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) environment variable key cross-platform + +It's usually `PATH`, but on Windows it can be any casing like `Path`... + + +## Install + +``` +$ npm install --save path-key +``` + + +## Usage + +```js +const pathKey = require('path-key'); + +const key = pathKey(); +//=> 'PATH' + +const PATH = process.env[key]; +//=> '/usr/local/bin:/usr/bin:/bin' +``` + + +## API + +### pathKey([options]) + +#### options + +##### env + +Type: `Object`
+Default: [`process.env`](https://nodejs.org/api/process.html#process_process_env) + +Use a custom environment variables object. + +#### platform + +Type: `string`
+Default: [`process.platform`](https://nodejs.org/api/process.html#process_process_platform) + +Get the PATH key for a specific platform. + + +## License + +MIT © [Sindre Sorhus](https://sindresorhus.com) diff --git a/node_modules/plist/.github/workflows/ci.yml b/node_modules/plist/.github/workflows/ci.yml new file mode 100644 index 0000000..f85c725 --- /dev/null +++ b/node_modules/plist/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: ci + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [ '14', '16', '18' ] + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - run: npm install + - run: npm test diff --git a/node_modules/plist/.jshintrc b/node_modules/plist/.jshintrc new file mode 100644 index 0000000..3f42622 --- /dev/null +++ b/node_modules/plist/.jshintrc @@ -0,0 +1,4 @@ +{ + "laxbreak": true, + "laxcomma": true +} diff --git a/node_modules/plist/History.md b/node_modules/plist/History.md new file mode 100644 index 0000000..fbec7c9 --- /dev/null +++ b/node_modules/plist/History.md @@ -0,0 +1,196 @@ +3.1.0 / 2023-07-06 +================== +* replace inlined xmldom with @xmldom/xmldom + + +3.0.5 / 2022-03-23 +================== +* [[`96e2303d05`]](https://github.com/TooTallNate/plist.js/commit/96e2303d059e6be0c9e0c4773226d14b4758de52) Prototype Pollution using .parse() #114 (mario-canva) +* update browserify from 16 to 17 + + +3.0.4 / 2021-08-27 +================== +* inline xmldom@0.6.0 to eliminate security warning false positive (Mike Reinstein) + + +3.0.3 / 2021-08-04 +================== +* update xmldom to 0.6.0 to patch critical vulnerability (Mike Reinstein) +* remove flaky saucelabs teseting badge (Mike Reinstein) + + +3.0.2 / 2021-03-25 +================== +* update xmldom to 0.5.0 to patch critical vulnerability (Mike Reinstein) +* update saucelab credentials to point at mreinstein's saucelabs account (Mike Reinstein) +* remove a bunch of test versions from the matrix because they weren't working in zuul + sauce (Mike Reinstein) + + +3.0.1 / 2018-03-21 +================== +* avoid using Buffer constructor nodejs/node#19079 + + +3.0.0 / 2018-03-18 +================== +* [[`cb0d8f1bc6`]](https://github.com/TooTallNate/plist.js/commit/cb0d8f1bc60dee423f8fc2cfcac427452dfaddb0) update Makefile, rebuild dist/ (Mike Reinstein) +* [[`9dfeffe73f`]](https://github.com/TooTallNate/plist.js/commit/9dfeffe73fdb447ac5a87ee364a1472975f12adf) remove unsupported browser versions from travis (Mike Reinstein) +* [[`c962bfe1ea`]](https://github.com/TooTallNate/plist.js/commit/c962bfe1ea04dd87966250253d33738bceecab61) update module deps, remove support for node < 6 (Mike Reinstein) + + +2.1.0 / 2017-05-04 +================== + +* [[`e4f06669bb`]](https://github.com/TooTallNate/plist.js/commit/e4f06669bb51d2e65654df7c39aab52bc3bf4e8a) - update license (extend copyright term) (Mike Reinstein) +* [[`edc6e41035`]](https://github.com/TooTallNate/plist.js/commit/edc6e4103546b1d7518a577e7c202c305a8abec0) - update module deps (Mike Reinstein) +* [[`85d11c48ef`](https://github.com/TooTallNate/plist.js/commit/85d11c48eff02312cbdd67f46fd8e74b0d372ca1)] - Harden test-cases and implementation to align with other implementations (Björn Brauer) +* [[`7619537eaa`]](https://github.com/TooTallNate/plist.js/commit/7619537eaa9e3e5a80829e759c004d2e017a07d2) review feedback: early returns and constants for nodeTypes (Björn Brauer) + + +2.0.1 / 2016-08-16 +================== + +* [[`de136c8388`](https://github.com/TooTallNate/plist/commit/de136c8388)] - bad npm release… (Nathan Rajlich) + + +2.0.0 / 2016-08-16 +================== + +* [[`90deef5d43`](https://github.com/TooTallNate/plist/commit/90deef5d43)] - remove deprecated functions (Nathan Rajlich) +* [[`d475cd8ce9`](https://github.com/TooTallNate/plist/commit/d475cd8ce9)] - Added travis ci support for node 6 (Amila Welihinda) +* [[`04c8ee7646`](https://github.com/TooTallNate/plist/commit/04c8ee7646)] - update dependencies (Mitchell Hentges) +* [[`97c02b3f05`](https://github.com/TooTallNate/plist/commit/97c02b3f05)] - **travis**: add `sudo: false` and test more node versions (Nathan Rajlich) +* [[`54c821ec29`](https://github.com/TooTallNate/plist/commit/54c821ec29)] - #71 - fixed and added test (Andrew Goldis) +* [[`4afb7c5079`](https://github.com/TooTallNate/plist/commit/4afb7c5079)] - fix `Cannot read property 'nodeValue' of undefined exception` that is thrown when a `` construct appears in plist (Chris Kinsman) +* [[`f360d7d685`](https://github.com/TooTallNate/plist/commit/f360d7d685)] - #66 - fixed empty keys and added tests (Andrew Goldis) +* [[`421c7f26e9`](https://github.com/TooTallNate/plist/commit/421c7f26e9)] - #66 - fixed empty key (Andrew Goldis) +* [[`a88aa4dca7`](https://github.com/TooTallNate/plist/commit/a88aa4dca7)] - add verbose examples (mrzmyr) + + +1.2.0 / 2015-11-10 +================== + + * package: update "browserify" to v12.0.1 + * package: update "zuul" to v3.7.2 + * package: update "xmlbuilder" to v4.0.0 + * package: update "util-deprecate" to v1.0.2 + * package: update "mocha" to v2.3.3 + * package: update "base64-js" to v0.0.8 + * build: omit undefined values + * travis: add node 4.0 and 4.1 to test matrix + + +1.1.0 / 2014-08-27 +================== + + * package: update "browserify" to v5.10.1 + * package: update "zuul" to v1.10.2 + * README: add "Sauce Test Status" build badge + * travis: use new "plistjs" sauce credentials + * travis: set up zuul saucelabs automated testing + + +1.0.1 / 2014-06-25 +================== + + * add .zuul.yml file for browser testing + * remove Testling stuff + * build: fix global variable `val` leak + * package: use --check-leaks when running mocha tests + * README: update examples to use preferred API + * package: add "browser" keyword + + +1.0.0 / 2014-05-20 +================== + + * package: remove "android-browser" + * test: add build() test + * test: re-add the empty string build() test + * test: remove "fixtures" and legacy "tests" dir + * test: add some more build() tests + * test: add a parse() CDATA test + * test: starting on build() tests + * test: more parse() tests + * package: attempt to fix "android-browser" testling + * parse: better with newline handling + * README: add Testling badge + * test: add node tests + * test: add a parse() test + * travis: don't test node v0.6 or v0.8 + * test: some more parse() tests + * test: add simple parsing test + * build: add support for an optional "opts" object + * package: test mobile devices + * test: use multiline to inline the XML + * package: beautify + * package: fix "mocha" harness + * package: more testling browsers + * build: add the "version=1.0" attribute + * beginnings of "mocha" tests + * build: more JSDocs + * tests: add test that ensures that empty string conversion works + * build: update "xmlbuilder" to v2.2.1 + * parse: ignore comment and cdata nodes + * tests: make the "Newlines" test actually contain a newline + * parse: lint + * test travis + * README: add Travis CI badge + * add .travis.yml file + * build: updated DTD to reflect name change + * parse: return falsey values in an Array plist + * build: fix encoding a typed array in the browser + * build: add support for Typed Arrays and ArrayBuffers + * build: more lint + * build: slight cleanup and optimizations + * build: use .txt() for the "date" value + * parse: always return a Buffer for nodes + * build: don't interpret Strings as base64 + * dist: commit prebuilt plist*.js files + * parse: fix typo in deprecate message + * parse: fix parse() return value + * parse: add jsdoc comments for the deprecated APIs + * parse: add `parse()` function + * node, parse: use `util-deprecate` module + * re-implemented parseFile to be asynchronous + * node: fix jsdoc comment + * Makefile: fix "node" require stubbing + * examples: add "browser" example + * package: tweak "main" + * package: remove "engines" field + * Makefile: fix --exclude command for browserify + * package: update "description" + * lib: more styling + * Makefile: add -build.js and -parse.js dist files + * lib: separate out the parse and build logic into their own files + * Makefile: add makefile with browserify build rules + * package: add "browserify" as a dev dependency + * plist: tabs to spaces (again) + * add a .jshintrc file + * LICENSE: update + * node-webkit support + * Ignore tests/ in .npmignore file + * Remove duplicate devDependencies key + * Remove trailing whitespace + * adding recent contributors. Bumping npm package number (patch release) + * Fixed node.js string handling + * bumping version number + * Fixed global variable plist leak + * patch release 0.4.1 + * removed temporary debug output file + * flipping the cases for writing data and string elements in build(). removed the 125 length check. Added validation of base64 encoding for data fields when parsing. added unit tests. + * fixed syntax errors in README examples (issue #20) + * added Sync versions of calls. added deprecation warnings for old method calls. updated documentation. If the resulting object from parseStringSync is an array with 1 element, return just the element. If a plist string or file doesnt have a tag as the document root element, fail noisily (issue #15) + * incrementing package version + * added cross platform base64 encode/decode for data elements (issue #17.) Comments and hygiene. + * refactored the code to use a DOM parser instead of SAX. closes issues #5 and #16 + * rolling up package version + * updated base64 detection regexp. updated README. hygiene. + * refactored the build function. Fixes issue #14 + * refactored tests. Modified tests from issue #9. thanks @sylvinus + * upgrade xmlbuilder package version. this is why .end() was needed in last commit; breaking change to xmlbuilder lib. :/ + * bug fix in build function, forgot to call .end() Refactored tests to use nodeunit + * Implemented support for real, identity tests + * Refactored base64 detection - still sloppy, fixed date building. Passing tests OK. + * Implemented basic plist builder that turns an existing JS object into plist XML. date, real and data types still need to be implemented. diff --git a/node_modules/plist/LICENSE b/node_modules/plist/LICENSE new file mode 100644 index 0000000..92e3423 --- /dev/null +++ b/node_modules/plist/LICENSE @@ -0,0 +1,24 @@ +(The MIT License) + +Copyright (c) 2010-2017 Nathan Rajlich + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/plist/Makefile b/node_modules/plist/Makefile new file mode 100644 index 0000000..7a918b5 --- /dev/null +++ b/node_modules/plist/Makefile @@ -0,0 +1,75 @@ + +# get Makefile directory name: http://stackoverflow.com/a/5982798/376773 +THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) + +# BIN directory +BIN := $(THIS_DIR)/node_modules/.bin + +# applications +NODE ?= node +NPM ?= $(NODE) $(shell which npm) +BROWSERIFY ?= $(NODE) $(BIN)/browserify +MOCHA ?= $(NODE) $(BIN)/mocha +ZUUL ?= $(NODE) $(BIN)/zuul + +REPORTER ?= spec + +all: dist/plist.js dist/plist-build.js dist/plist-parse.js + +install: node_modules + +clean: + @rm -rf node_modules dist + +dist: + @mkdir -p $@ + +dist/plist-build.js: node_modules lib/build.js dist + @$(BROWSERIFY) \ + --standalone plist \ + lib/build.js > $@ + +dist/plist-parse.js: node_modules lib/parse.js dist + @$(BROWSERIFY) \ + --standalone plist \ + lib/parse.js > $@ + +dist/plist.js: node_modules lib/*.js dist + @$(BROWSERIFY) \ + --standalone plist \ + index.js > $@ + +node_modules: package.json + @NODE_ENV= $(NPM) install + @touch node_modules + +test: + @if [ "x$(BROWSER_NAME)" = "x" ]; then \ + $(MAKE) test-node; \ + else \ + $(MAKE) test-zuul; \ + fi + +test-node: + @$(MOCHA) \ + --reporter $(REPORTER) \ + test/*.js + +test-zuul: + @if [ "x$(BROWSER_PLATFORM)" = "x" ]; then \ + $(ZUUL) \ + --ui mocha-bdd \ + --browser-name $(BROWSER_NAME) \ + --browser-version $(BROWSER_VERSION) \ + test/*.js; \ + else \ + $(ZUUL) \ + --ui mocha-bdd \ + --browser-name $(BROWSER_NAME) \ + --browser-version $(BROWSER_VERSION) \ + --browser-platform "$(BROWSER_PLATFORM)" \ + test/*.js; \ + fi + +.PHONY: all install clean test test-node test-zuul diff --git a/node_modules/plist/README.md b/node_modules/plist/README.md new file mode 100644 index 0000000..bb6fcf9 --- /dev/null +++ b/node_modules/plist/README.md @@ -0,0 +1,146 @@ +plist.js +======== +### Apple's Property list parser/builder for Node.js and browsers + +[![ci](https://github.com/TooTallNate/plist.js/actions/workflows/ci.yml/badge.svg)](https://github.com/TooTallNate/plist.js/actions/workflows/ci.yml) + +Provides facilities for reading and writing Plist (property list) files. +These are often used in programming OS X and iOS applications, as well +as the iTunes configuration XML file. + +Plist files represent stored programming "object"s. They are very similar +to JSON. A valid Plist file is representable as a native JavaScript Object +and vice-versa. + + +## Usage + +### Node.js + +Install using `npm`: + +``` bash +$ npm install --save plist +``` + +Then `require()` the _plist_ module in your file: + +``` js +var plist = require('plist'); + +// now use the `parse()` and `build()` functions +var val = plist.parse('Hello World!'); +console.log(val); // "Hello World!" +``` + + +### Browser + +Include the `dist/plist.js` in a ` + +``` + + +## API + +### Parsing + +Parsing a plist from filename: + +``` javascript +var fs = require('fs'); +var plist = require('plist'); + +var obj = plist.parse(fs.readFileSync('myPlist.plist', 'utf8')); +console.log(JSON.stringify(obj)); +``` + +Parsing a plist from string payload: + +``` javascript +var plist = require('plist'); + +var xml = + '' + + '' + + '' + + 'metadata' + + '' + + 'bundle-identifier' + + 'com.company.app' + + 'bundle-version' + + '0.1.1' + + 'kind' + + 'software' + + 'title' + + 'AppName' + + '' + + ''; + +console.log(plist.parse(xml)); + +// [ +// "metadata", +// { +// "bundle-identifier": "com.company.app", +// "bundle-version": "0.1.1", +// "kind": "software", +// "title": "AppName" +// } +// ] +``` + +### Building + +Given an existing JavaScript Object, you can turn it into an XML document +that complies with the plist DTD: + +``` javascript +var plist = require('plist'); + +var json = [ + "metadata", + { + "bundle-identifier": "com.company.app", + "bundle-version": "0.1.1", + "kind": "software", + "title": "AppName" + } +]; + +console.log(plist.build(json)); + +// +// +// +// metadata +// +// bundle-identifier +// com.company.app +// bundle-version +// 0.1.1 +// kind +// software +// title +// AppName +// +// +``` + +## Cross Platform Testing Credits + +Much thanks to Sauce Labs for providing free resources that enable cross-browser testing on this project! + +[![Testing Powered By SauceLabs](https://opensource.saucelabs.com/images/opensauce/powered-by-saucelabs-badge-red.png?sanitize=true "Testing Powered By SauceLabs")](https://saucelabs.com) + + +## License + +[(The MIT License)](LICENSE) diff --git a/node_modules/plist/dist/plist-build.js b/node_modules/plist/dist/plist-build.js new file mode 100644 index 0000000..0eeef51 --- /dev/null +++ b/node_modules/plist/dist/plist-build.js @@ -0,0 +1,5348 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.plist = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + var validLen = b64.indexOf('=') + if (validLen === -1) validLen = len + + var placeHoldersLen = validLen === len + ? 0 + : 4 - (validLen % 4) + + return [validLen, placeHoldersLen] +} + +// base64 is 4/3 + up to two characters of the original data +function byteLength (b64) { + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function _byteLength (b64, validLen, placeHoldersLen) { + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function toByteArray (b64) { + var tmp + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) + + var curByte = 0 + + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 + ? validLen - 4 + : validLen + + var i + for (i = 0; i < len; i += 4) { + tmp = + (revLookup[b64.charCodeAt(i)] << 18) | + (revLookup[b64.charCodeAt(i + 1)] << 12) | + (revLookup[b64.charCodeAt(i + 2)] << 6) | + revLookup[b64.charCodeAt(i + 3)] + arr[curByte++] = (tmp >> 16) & 0xFF + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 2) { + tmp = + (revLookup[b64.charCodeAt(i)] << 2) | + (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 1) { + tmp = + (revLookup[b64.charCodeAt(i)] << 10) | + (revLookup[b64.charCodeAt(i + 1)] << 4) | + (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + + lookup[num >> 12 & 0x3F] + + lookup[num >> 6 & 0x3F] + + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + parts.push( + lookup[tmp >> 2] + + lookup[(tmp << 4) & 0x3F] + + '==' + ) + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1] + parts.push( + lookup[tmp >> 10] + + lookup[(tmp >> 4) & 0x3F] + + lookup[(tmp << 2) & 0x3F] + + '=' + ) + } + + return parts.join('') +} + +},{}],3:[function(require,module,exports){ +/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */ + +// The _isBuffer check is for Safari 5-7 support, because it's missing +// Object.prototype.constructor. Remove this eventually +module.exports = function (obj) { + return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) +} + +function isBuffer (obj) { + return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} + +// For Node v0.10 support. Remove this eventually. +function isSlowBuffer (obj) { + return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) +} + +},{}],4:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + module.exports = { + Disconnected: 1, + Preceding: 2, + Following: 4, + Contains: 8, + ContainedBy: 16, + ImplementationSpecific: 32 + }; + +}).call(this); + +},{}],5:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + module.exports = { + Element: 1, + Attribute: 2, + Text: 3, + CData: 4, + EntityReference: 5, + EntityDeclaration: 6, + ProcessingInstruction: 7, + Comment: 8, + Document: 9, + DocType: 10, + DocumentFragment: 11, + NotationDeclaration: 12, + // Numeric codes up to 200 are reserved to W3C for possible future use. + // Following are types internal to this library: + Declaration: 201, + Raw: 202, + AttributeDeclaration: 203, + ElementDeclaration: 204, + Dummy: 205 + }; + +}).call(this); + +},{}],6:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + // Copies all enumerable own properties from `sources` to `target` + var assign, getValue, isArray, isEmpty, isFunction, isObject, isPlainObject, + hasProp = {}.hasOwnProperty; + + assign = function(target, ...sources) { + var i, key, len, source; + if (isFunction(Object.assign)) { + Object.assign.apply(null, arguments); + } else { + for (i = 0, len = sources.length; i < len; i++) { + source = sources[i]; + if (source != null) { + for (key in source) { + if (!hasProp.call(source, key)) continue; + target[key] = source[key]; + } + } + } + } + return target; + }; + + // Determines if `val` is a Function object + isFunction = function(val) { + return !!val && Object.prototype.toString.call(val) === '[object Function]'; + }; + + // Determines if `val` is an Object + isObject = function(val) { + var ref; + return !!val && ((ref = typeof val) === 'function' || ref === 'object'); + }; + + // Determines if `val` is an Array + isArray = function(val) { + if (isFunction(Array.isArray)) { + return Array.isArray(val); + } else { + return Object.prototype.toString.call(val) === '[object Array]'; + } + }; + + // Determines if `val` is an empty Array or an Object with no own properties + isEmpty = function(val) { + var key; + if (isArray(val)) { + return !val.length; + } else { + for (key in val) { + if (!hasProp.call(val, key)) continue; + return false; + } + return true; + } + }; + + // Determines if `val` is a plain Object + isPlainObject = function(val) { + var ctor, proto; + return isObject(val) && (proto = Object.getPrototypeOf(val)) && (ctor = proto.constructor) && (typeof ctor === 'function') && (ctor instanceof ctor) && (Function.prototype.toString.call(ctor) === Function.prototype.toString.call(Object)); + }; + + // Gets the primitive value of an object + getValue = function(obj) { + if (isFunction(obj.valueOf)) { + return obj.valueOf(); + } else { + return obj; + } + }; + + module.exports.assign = assign; + + module.exports.isFunction = isFunction; + + module.exports.isObject = isObject; + + module.exports.isArray = isArray; + + module.exports.isEmpty = isEmpty; + + module.exports.isPlainObject = isPlainObject; + + module.exports.getValue = getValue; + +}).call(this); + +},{}],7:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + module.exports = { + None: 0, + OpenTag: 1, + InsideTag: 2, + CloseTag: 3 + }; + +}).call(this); + +},{}],8:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLAttribute, XMLNode; + + NodeType = require('./NodeType'); + + XMLNode = require('./XMLNode'); + + // Represents an attribute + module.exports = XMLAttribute = (function() { + class XMLAttribute { + // Initializes a new instance of `XMLAttribute` + + // `parent` the parent node + // `name` attribute target + // `value` attribute value + constructor(parent, name, value) { + this.parent = parent; + if (this.parent) { + this.options = this.parent.options; + this.stringify = this.parent.stringify; + } + if (name == null) { + throw new Error("Missing attribute name. " + this.debugInfo(name)); + } + this.name = this.stringify.name(name); + this.value = this.stringify.attValue(value); + this.type = NodeType.Attribute; + // DOM level 3 + this.isId = false; + this.schemaTypeInfo = null; + } + + // Creates and returns a deep clone of `this` + clone() { + return Object.create(this); + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.attribute(this, this.options.writer.filterOptions(options)); + } + + + // Returns debug string for this node + debugInfo(name) { + name = name || this.name; + if (name == null) { + return "parent: <" + this.parent.name + ">"; + } else { + return "attribute: {" + name + "}, parent: <" + this.parent.name + ">"; + } + } + + isEqualNode(node) { + if (node.namespaceURI !== this.namespaceURI) { + return false; + } + if (node.prefix !== this.prefix) { + return false; + } + if (node.localName !== this.localName) { + return false; + } + if (node.value !== this.value) { + return false; + } + return true; + } + + }; + + // DOM level 1 + Object.defineProperty(XMLAttribute.prototype, 'nodeType', { + get: function() { + return this.type; + } + }); + + Object.defineProperty(XMLAttribute.prototype, 'ownerElement', { + get: function() { + return this.parent; + } + }); + + // DOM level 3 + Object.defineProperty(XMLAttribute.prototype, 'textContent', { + get: function() { + return this.value; + }, + set: function(value) { + return this.value = value || ''; + } + }); + + // DOM level 4 + Object.defineProperty(XMLAttribute.prototype, 'namespaceURI', { + get: function() { + return ''; + } + }); + + Object.defineProperty(XMLAttribute.prototype, 'prefix', { + get: function() { + return ''; + } + }); + + Object.defineProperty(XMLAttribute.prototype, 'localName', { + get: function() { + return this.name; + } + }); + + Object.defineProperty(XMLAttribute.prototype, 'specified', { + get: function() { + return true; + } + }); + + return XMLAttribute; + + }).call(this); + +}).call(this); + +},{"./NodeType":5,"./XMLNode":27}],9:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLCData, XMLCharacterData; + + NodeType = require('./NodeType'); + + XMLCharacterData = require('./XMLCharacterData'); + + // Represents a CDATA node + module.exports = XMLCData = class XMLCData extends XMLCharacterData { + // Initializes a new instance of `XMLCData` + + // `text` CDATA text + constructor(parent, text) { + super(parent); + if (text == null) { + throw new Error("Missing CDATA text. " + this.debugInfo()); + } + this.name = "#cdata-section"; + this.type = NodeType.CData; + this.value = this.stringify.cdata(text); + } + + // Creates and returns a deep clone of `this` + clone() { + return Object.create(this); + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.cdata(this, this.options.writer.filterOptions(options)); + } + + }; + +}).call(this); + +},{"./NodeType":5,"./XMLCharacterData":10}],10:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var XMLCharacterData, XMLNode; + + XMLNode = require('./XMLNode'); + + // Represents a character data node + module.exports = XMLCharacterData = (function() { + class XMLCharacterData extends XMLNode { + // Initializes a new instance of `XMLCharacterData` + + constructor(parent) { + super(parent); + this.value = ''; + } + + + // Creates and returns a deep clone of `this` + clone() { + return Object.create(this); + } + + // DOM level 1 functions to be implemented later + substringData(offset, count) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + appendData(arg) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + insertData(offset, arg) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + deleteData(offset, count) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + replaceData(offset, count, arg) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + isEqualNode(node) { + if (!super.isEqualNode(node)) { + return false; + } + if (node.data !== this.data) { + return false; + } + return true; + } + + }; + + // DOM level 1 + Object.defineProperty(XMLCharacterData.prototype, 'data', { + get: function() { + return this.value; + }, + set: function(value) { + return this.value = value || ''; + } + }); + + Object.defineProperty(XMLCharacterData.prototype, 'length', { + get: function() { + return this.value.length; + } + }); + + // DOM level 3 + Object.defineProperty(XMLCharacterData.prototype, 'textContent', { + get: function() { + return this.value; + }, + set: function(value) { + return this.value = value || ''; + } + }); + + return XMLCharacterData; + + }).call(this); + +}).call(this); + +},{"./XMLNode":27}],11:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLCharacterData, XMLComment; + + NodeType = require('./NodeType'); + + XMLCharacterData = require('./XMLCharacterData'); + + // Represents a comment node + module.exports = XMLComment = class XMLComment extends XMLCharacterData { + // Initializes a new instance of `XMLComment` + + // `text` comment text + constructor(parent, text) { + super(parent); + if (text == null) { + throw new Error("Missing comment text. " + this.debugInfo()); + } + this.name = "#comment"; + this.type = NodeType.Comment; + this.value = this.stringify.comment(text); + } + + // Creates and returns a deep clone of `this` + clone() { + return Object.create(this); + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.comment(this, this.options.writer.filterOptions(options)); + } + + }; + +}).call(this); + +},{"./NodeType":5,"./XMLCharacterData":10}],12:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var XMLDOMConfiguration, XMLDOMErrorHandler, XMLDOMStringList; + + XMLDOMErrorHandler = require('./XMLDOMErrorHandler'); + + XMLDOMStringList = require('./XMLDOMStringList'); + + // Implements the DOMConfiguration interface + module.exports = XMLDOMConfiguration = (function() { + class XMLDOMConfiguration { + constructor() { + var clonedSelf; + this.defaultParams = { + "canonical-form": false, + "cdata-sections": false, + "comments": false, + "datatype-normalization": false, + "element-content-whitespace": true, + "entities": true, + "error-handler": new XMLDOMErrorHandler(), + "infoset": true, + "validate-if-schema": false, + "namespaces": true, + "namespace-declarations": true, + "normalize-characters": false, + "schema-location": '', + "schema-type": '', + "split-cdata-sections": true, + "validate": false, + "well-formed": true + }; + this.params = clonedSelf = Object.create(this.defaultParams); + } + + // Gets the value of a parameter. + + // `name` name of the parameter + getParameter(name) { + if (this.params.hasOwnProperty(name)) { + return this.params[name]; + } else { + return null; + } + } + + // Checks if setting a parameter to a specific value is supported. + + // `name` name of the parameter + // `value` parameter value + canSetParameter(name, value) { + return true; + } + + // Sets the value of a parameter. + + // `name` name of the parameter + // `value` new value or null if the user wishes to unset the parameter + setParameter(name, value) { + if (value != null) { + return this.params[name] = value; + } else { + return delete this.params[name]; + } + } + + }; + + // Returns the list of parameter names + Object.defineProperty(XMLDOMConfiguration.prototype, 'parameterNames', { + get: function() { + return new XMLDOMStringList(Object.keys(this.defaultParams)); + } + }); + + return XMLDOMConfiguration; + + }).call(this); + +}).call(this); + +},{"./XMLDOMErrorHandler":13,"./XMLDOMStringList":15}],13:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + // Represents the error handler for DOM operations + var XMLDOMErrorHandler; + + module.exports = XMLDOMErrorHandler = class XMLDOMErrorHandler { + // Initializes a new instance of `XMLDOMErrorHandler` + + constructor() {} + + // Called on the error handler when an error occurs. + + // `error` the error message as a string + handleError(error) { + throw new Error(error); + } + + }; + +}).call(this); + +},{}],14:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + // Implements the DOMImplementation interface + var XMLDOMImplementation; + + module.exports = XMLDOMImplementation = class XMLDOMImplementation { + // Tests if the DOM implementation implements a specific feature. + + // `feature` package name of the feature to test. In Level 1, the + // legal values are "HTML" and "XML" (case-insensitive). + // `version` version number of the package name to test. + // In Level 1, this is the string "1.0". If the version is + // not specified, supporting any version of the feature will + // cause the method to return true. + hasFeature(feature, version) { + return true; + } + + // Creates a new document type declaration. + + // `qualifiedName` qualified name of the document type to be created + // `publicId` public identifier of the external subset + // `systemId` system identifier of the external subset + createDocumentType(qualifiedName, publicId, systemId) { + throw new Error("This DOM method is not implemented."); + } + + // Creates a new document. + + // `namespaceURI` namespace URI of the document element to create + // `qualifiedName` the qualified name of the document to be created + // `doctype` the type of document to be created or null + createDocument(namespaceURI, qualifiedName, doctype) { + throw new Error("This DOM method is not implemented."); + } + + // Creates a new HTML document. + + // `title` document title + createHTMLDocument(title) { + throw new Error("This DOM method is not implemented."); + } + + // Returns a specialized object which implements the specialized APIs + // of the specified feature and version. + + // `feature` name of the feature requested. + // `version` version number of the feature to test + getFeature(feature, version) { + throw new Error("This DOM method is not implemented."); + } + + }; + +}).call(this); + +},{}],15:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + // Represents a list of string entries + var XMLDOMStringList; + + module.exports = XMLDOMStringList = (function() { + class XMLDOMStringList { + // Initializes a new instance of `XMLDOMStringList` + // This is just a wrapper around an ordinary + // JS array. + + // `arr` the array of string values + constructor(arr) { + this.arr = arr || []; + } + + // Returns the indexth item in the collection. + + // `index` index into the collection + item(index) { + return this.arr[index] || null; + } + + // Test if a string is part of this DOMStringList. + + // `str` the string to look for + contains(str) { + return this.arr.indexOf(str) !== -1; + } + + }; + + // Returns the number of strings in the list. + Object.defineProperty(XMLDOMStringList.prototype, 'length', { + get: function() { + return this.arr.length; + } + }); + + return XMLDOMStringList; + + }).call(this); + +}).call(this); + +},{}],16:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLDTDAttList, XMLNode; + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + // Represents an attribute list + module.exports = XMLDTDAttList = class XMLDTDAttList extends XMLNode { + // Initializes a new instance of `XMLDTDAttList` + + // `parent` the parent `XMLDocType` element + // `elementName` the name of the element containing this attribute + // `attributeName` attribute name + // `attributeType` type of the attribute + // `defaultValueType` default value type (either #REQUIRED, #IMPLIED, + // #FIXED or #DEFAULT) + // `defaultValue` default value of the attribute + // (only used for #FIXED or #DEFAULT) + constructor(parent, elementName, attributeName, attributeType, defaultValueType, defaultValue) { + super(parent); + if (elementName == null) { + throw new Error("Missing DTD element name. " + this.debugInfo()); + } + if (attributeName == null) { + throw new Error("Missing DTD attribute name. " + this.debugInfo(elementName)); + } + if (!attributeType) { + throw new Error("Missing DTD attribute type. " + this.debugInfo(elementName)); + } + if (!defaultValueType) { + throw new Error("Missing DTD attribute default. " + this.debugInfo(elementName)); + } + if (defaultValueType.indexOf('#') !== 0) { + defaultValueType = '#' + defaultValueType; + } + if (!defaultValueType.match(/^(#REQUIRED|#IMPLIED|#FIXED|#DEFAULT)$/)) { + throw new Error("Invalid default value type; expected: #REQUIRED, #IMPLIED, #FIXED or #DEFAULT. " + this.debugInfo(elementName)); + } + if (defaultValue && !defaultValueType.match(/^(#FIXED|#DEFAULT)$/)) { + throw new Error("Default value only applies to #FIXED or #DEFAULT. " + this.debugInfo(elementName)); + } + this.elementName = this.stringify.name(elementName); + this.type = NodeType.AttributeDeclaration; + this.attributeName = this.stringify.name(attributeName); + this.attributeType = this.stringify.dtdAttType(attributeType); + if (defaultValue) { + this.defaultValue = this.stringify.dtdAttDefault(defaultValue); + } + this.defaultValueType = defaultValueType; + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.dtdAttList(this, this.options.writer.filterOptions(options)); + } + + }; + +}).call(this); + +},{"./NodeType":5,"./XMLNode":27}],17:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLDTDElement, XMLNode; + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + // Represents an attribute + module.exports = XMLDTDElement = class XMLDTDElement extends XMLNode { + // Initializes a new instance of `XMLDTDElement` + + // `parent` the parent `XMLDocType` element + // `name` element name + // `value` element content (defaults to #PCDATA) + constructor(parent, name, value) { + super(parent); + if (name == null) { + throw new Error("Missing DTD element name. " + this.debugInfo()); + } + if (!value) { + value = '(#PCDATA)'; + } + if (Array.isArray(value)) { + value = '(' + value.join(',') + ')'; + } + this.name = this.stringify.name(name); + this.type = NodeType.ElementDeclaration; + this.value = this.stringify.dtdElementValue(value); + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.dtdElement(this, this.options.writer.filterOptions(options)); + } + + }; + +}).call(this); + +},{"./NodeType":5,"./XMLNode":27}],18:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLDTDEntity, XMLNode, isObject; + + ({isObject} = require('./Utility')); + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + // Represents an entity declaration in the DTD + module.exports = XMLDTDEntity = (function() { + class XMLDTDEntity extends XMLNode { + // Initializes a new instance of `XMLDTDEntity` + + // `parent` the parent `XMLDocType` element + // `pe` whether this is a parameter entity or a general entity + // defaults to `false` (general entity) + // `name` the name of the entity + // `value` internal entity value or an object with external entity details + // `value.pubID` public identifier + // `value.sysID` system identifier + // `value.nData` notation declaration + constructor(parent, pe, name, value) { + super(parent); + if (name == null) { + throw new Error("Missing DTD entity name. " + this.debugInfo(name)); + } + if (value == null) { + throw new Error("Missing DTD entity value. " + this.debugInfo(name)); + } + this.pe = !!pe; + this.name = this.stringify.name(name); + this.type = NodeType.EntityDeclaration; + if (!isObject(value)) { + this.value = this.stringify.dtdEntityValue(value); + this.internal = true; + } else { + if (!value.pubID && !value.sysID) { + throw new Error("Public and/or system identifiers are required for an external entity. " + this.debugInfo(name)); + } + if (value.pubID && !value.sysID) { + throw new Error("System identifier is required for a public external entity. " + this.debugInfo(name)); + } + this.internal = false; + if (value.pubID != null) { + this.pubID = this.stringify.dtdPubID(value.pubID); + } + if (value.sysID != null) { + this.sysID = this.stringify.dtdSysID(value.sysID); + } + if (value.nData != null) { + this.nData = this.stringify.dtdNData(value.nData); + } + if (this.pe && this.nData) { + throw new Error("Notation declaration is not allowed in a parameter entity. " + this.debugInfo(name)); + } + } + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.dtdEntity(this, this.options.writer.filterOptions(options)); + } + + }; + + // DOM level 1 + Object.defineProperty(XMLDTDEntity.prototype, 'publicId', { + get: function() { + return this.pubID; + } + }); + + Object.defineProperty(XMLDTDEntity.prototype, 'systemId', { + get: function() { + return this.sysID; + } + }); + + Object.defineProperty(XMLDTDEntity.prototype, 'notationName', { + get: function() { + return this.nData || null; + } + }); + + // DOM level 3 + Object.defineProperty(XMLDTDEntity.prototype, 'inputEncoding', { + get: function() { + return null; + } + }); + + Object.defineProperty(XMLDTDEntity.prototype, 'xmlEncoding', { + get: function() { + return null; + } + }); + + Object.defineProperty(XMLDTDEntity.prototype, 'xmlVersion', { + get: function() { + return null; + } + }); + + return XMLDTDEntity; + + }).call(this); + +}).call(this); + +},{"./NodeType":5,"./Utility":6,"./XMLNode":27}],19:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLDTDNotation, XMLNode; + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + // Represents a NOTATION entry in the DTD + module.exports = XMLDTDNotation = (function() { + class XMLDTDNotation extends XMLNode { + // Initializes a new instance of `XMLDTDNotation` + + // `parent` the parent `XMLDocType` element + // `name` the name of the notation + // `value` an object with external entity details + // `value.pubID` public identifier + // `value.sysID` system identifier + constructor(parent, name, value) { + super(parent); + if (name == null) { + throw new Error("Missing DTD notation name. " + this.debugInfo(name)); + } + if (!value.pubID && !value.sysID) { + throw new Error("Public or system identifiers are required for an external entity. " + this.debugInfo(name)); + } + this.name = this.stringify.name(name); + this.type = NodeType.NotationDeclaration; + if (value.pubID != null) { + this.pubID = this.stringify.dtdPubID(value.pubID); + } + if (value.sysID != null) { + this.sysID = this.stringify.dtdSysID(value.sysID); + } + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.dtdNotation(this, this.options.writer.filterOptions(options)); + } + + }; + + // DOM level 1 + Object.defineProperty(XMLDTDNotation.prototype, 'publicId', { + get: function() { + return this.pubID; + } + }); + + Object.defineProperty(XMLDTDNotation.prototype, 'systemId', { + get: function() { + return this.sysID; + } + }); + + return XMLDTDNotation; + + }).call(this); + +}).call(this); + +},{"./NodeType":5,"./XMLNode":27}],20:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLDeclaration, XMLNode, isObject; + + ({isObject} = require('./Utility')); + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + // Represents the XML declaration + module.exports = XMLDeclaration = class XMLDeclaration extends XMLNode { + // Initializes a new instance of `XMLDeclaration` + + // `parent` the document object + + // `version` A version number string, e.g. 1.0 + // `encoding` Encoding declaration, e.g. UTF-8 + // `standalone` standalone document declaration: true or false + constructor(parent, version, encoding, standalone) { + super(parent); + // arguments may also be passed as an object + if (isObject(version)) { + ({version, encoding, standalone} = version); + } + if (!version) { + version = '1.0'; + } + this.type = NodeType.Declaration; + this.version = this.stringify.xmlVersion(version); + if (encoding != null) { + this.encoding = this.stringify.xmlEncoding(encoding); + } + if (standalone != null) { + this.standalone = this.stringify.xmlStandalone(standalone); + } + } + + // Converts to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.declaration(this, this.options.writer.filterOptions(options)); + } + + }; + +}).call(this); + +},{"./NodeType":5,"./Utility":6,"./XMLNode":27}],21:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLDTDAttList, XMLDTDElement, XMLDTDEntity, XMLDTDNotation, XMLDocType, XMLNamedNodeMap, XMLNode, isObject; + + ({isObject} = require('./Utility')); + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + XMLDTDAttList = require('./XMLDTDAttList'); + + XMLDTDEntity = require('./XMLDTDEntity'); + + XMLDTDElement = require('./XMLDTDElement'); + + XMLDTDNotation = require('./XMLDTDNotation'); + + XMLNamedNodeMap = require('./XMLNamedNodeMap'); + + // Represents doctype declaration + module.exports = XMLDocType = (function() { + class XMLDocType extends XMLNode { + // Initializes a new instance of `XMLDocType` + + // `parent` the document object + + // `pubID` public identifier of the external subset + // `sysID` system identifier of the external subset + constructor(parent, pubID, sysID) { + var child, i, len, ref; + super(parent); + this.type = NodeType.DocType; + // set DTD name to the name of the root node + if (parent.children) { + ref = parent.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + if (child.type === NodeType.Element) { + this.name = child.name; + break; + } + } + } + this.documentObject = parent; + // arguments may also be passed as an object + if (isObject(pubID)) { + ({pubID, sysID} = pubID); + } + if (sysID == null) { + [sysID, pubID] = [pubID, sysID]; + } + if (pubID != null) { + this.pubID = this.stringify.dtdPubID(pubID); + } + if (sysID != null) { + this.sysID = this.stringify.dtdSysID(sysID); + } + } + + // Creates an element type declaration + + // `name` element name + // `value` element content (defaults to #PCDATA) + element(name, value) { + var child; + child = new XMLDTDElement(this, name, value); + this.children.push(child); + return this; + } + + // Creates an attribute declaration + + // `elementName` the name of the element containing this attribute + // `attributeName` attribute name + // `attributeType` type of the attribute (defaults to CDATA) + // `defaultValueType` default value type (either #REQUIRED, #IMPLIED, #FIXED or + // #DEFAULT) (defaults to #IMPLIED) + // `defaultValue` default value of the attribute + // (only used for #FIXED or #DEFAULT) + attList(elementName, attributeName, attributeType, defaultValueType, defaultValue) { + var child; + child = new XMLDTDAttList(this, elementName, attributeName, attributeType, defaultValueType, defaultValue); + this.children.push(child); + return this; + } + + // Creates a general entity declaration + + // `name` the name of the entity + // `value` internal entity value or an object with external entity details + // `value.pubID` public identifier + // `value.sysID` system identifier + // `value.nData` notation declaration + entity(name, value) { + var child; + child = new XMLDTDEntity(this, false, name, value); + this.children.push(child); + return this; + } + + // Creates a parameter entity declaration + + // `name` the name of the entity + // `value` internal entity value or an object with external entity details + // `value.pubID` public identifier + // `value.sysID` system identifier + pEntity(name, value) { + var child; + child = new XMLDTDEntity(this, true, name, value); + this.children.push(child); + return this; + } + + // Creates a NOTATION declaration + + // `name` the name of the notation + // `value` an object with external entity details + // `value.pubID` public identifier + // `value.sysID` system identifier + notation(name, value) { + var child; + child = new XMLDTDNotation(this, name, value); + this.children.push(child); + return this; + } + + // Converts to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.docType(this, this.options.writer.filterOptions(options)); + } + + // Aliases + ele(name, value) { + return this.element(name, value); + } + + att(elementName, attributeName, attributeType, defaultValueType, defaultValue) { + return this.attList(elementName, attributeName, attributeType, defaultValueType, defaultValue); + } + + ent(name, value) { + return this.entity(name, value); + } + + pent(name, value) { + return this.pEntity(name, value); + } + + not(name, value) { + return this.notation(name, value); + } + + up() { + return this.root() || this.documentObject; + } + + isEqualNode(node) { + if (!super.isEqualNode(node)) { + return false; + } + if (node.name !== this.name) { + return false; + } + if (node.publicId !== this.publicId) { + return false; + } + if (node.systemId !== this.systemId) { + return false; + } + return true; + } + + }; + + // DOM level 1 + Object.defineProperty(XMLDocType.prototype, 'entities', { + get: function() { + var child, i, len, nodes, ref; + nodes = {}; + ref = this.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + if ((child.type === NodeType.EntityDeclaration) && !child.pe) { + nodes[child.name] = child; + } + } + return new XMLNamedNodeMap(nodes); + } + }); + + Object.defineProperty(XMLDocType.prototype, 'notations', { + get: function() { + var child, i, len, nodes, ref; + nodes = {}; + ref = this.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + if (child.type === NodeType.NotationDeclaration) { + nodes[child.name] = child; + } + } + return new XMLNamedNodeMap(nodes); + } + }); + + // DOM level 2 + Object.defineProperty(XMLDocType.prototype, 'publicId', { + get: function() { + return this.pubID; + } + }); + + Object.defineProperty(XMLDocType.prototype, 'systemId', { + get: function() { + return this.sysID; + } + }); + + Object.defineProperty(XMLDocType.prototype, 'internalSubset', { + get: function() { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + }); + + return XMLDocType; + + }).call(this); + +}).call(this); + +},{"./NodeType":5,"./Utility":6,"./XMLDTDAttList":16,"./XMLDTDElement":17,"./XMLDTDEntity":18,"./XMLDTDNotation":19,"./XMLNamedNodeMap":26,"./XMLNode":27}],22:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLDOMConfiguration, XMLDOMImplementation, XMLDocument, XMLNode, XMLStringWriter, XMLStringifier, isPlainObject; + + ({isPlainObject} = require('./Utility')); + + XMLDOMImplementation = require('./XMLDOMImplementation'); + + XMLDOMConfiguration = require('./XMLDOMConfiguration'); + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + XMLStringifier = require('./XMLStringifier'); + + XMLStringWriter = require('./XMLStringWriter'); + + // Represents an XML builder + module.exports = XMLDocument = (function() { + class XMLDocument extends XMLNode { + // Initializes a new instance of `XMLDocument` + + // `options.keepNullNodes` whether nodes with null values will be kept + // or ignored: true or false + // `options.keepNullAttributes` whether attributes with null values will be + // kept or ignored: true or false + // `options.ignoreDecorators` whether decorator strings will be ignored when + // converting JS objects: true or false + // `options.separateArrayItems` whether array items are created as separate + // nodes when passed as an object value: true or false + // `options.noDoubleEncoding` whether existing html entities are encoded: + // true or false + // `options.stringify` a set of functions to use for converting values to + // strings + // `options.writer` the default XML writer to use for converting nodes to + // string. If the default writer is not set, the built-in XMLStringWriter + // will be used instead. + constructor(options) { + super(null); + this.name = "#document"; + this.type = NodeType.Document; + this.documentURI = null; + this.domConfig = new XMLDOMConfiguration(); + options || (options = {}); + if (!options.writer) { + options.writer = new XMLStringWriter(); + } + this.options = options; + this.stringify = new XMLStringifier(options); + } + + // Ends the document and passes it to the given XML writer + + // `writer` is either an XML writer or a plain object to pass to the + // constructor of the default XML writer. The default writer is assigned when + // creating the XML document. Following flags are recognized by the + // built-in XMLStringWriter: + // `writer.pretty` pretty prints the result + // `writer.indent` indentation for pretty print + // `writer.offset` how many indentations to add to every line for pretty print + // `writer.newline` newline sequence for pretty print + end(writer) { + var writerOptions; + writerOptions = {}; + if (!writer) { + writer = this.options.writer; + } else if (isPlainObject(writer)) { + writerOptions = writer; + writer = this.options.writer; + } + return writer.document(this, writer.filterOptions(writerOptions)); + } + + // Converts the XML document to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.document(this, this.options.writer.filterOptions(options)); + } + + // DOM level 1 functions to be implemented later + createElement(tagName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createDocumentFragment() { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createTextNode(data) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createComment(data) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createCDATASection(data) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createProcessingInstruction(target, data) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createAttribute(name) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createEntityReference(name) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getElementsByTagName(tagname) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + // DOM level 2 functions to be implemented later + importNode(importedNode, deep) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createElementNS(namespaceURI, qualifiedName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createAttributeNS(namespaceURI, qualifiedName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getElementsByTagNameNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getElementById(elementId) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + // DOM level 3 functions to be implemented later + adoptNode(source) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + normalizeDocument() { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + renameNode(node, namespaceURI, qualifiedName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + // DOM level 4 functions to be implemented later + getElementsByClassName(classNames) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createEvent(eventInterface) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createRange() { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createNodeIterator(root, whatToShow, filter) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + createTreeWalker(root, whatToShow, filter) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + }; + + // DOM level 1 + Object.defineProperty(XMLDocument.prototype, 'implementation', { + value: new XMLDOMImplementation() + }); + + Object.defineProperty(XMLDocument.prototype, 'doctype', { + get: function() { + var child, i, len, ref; + ref = this.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + if (child.type === NodeType.DocType) { + return child; + } + } + return null; + } + }); + + Object.defineProperty(XMLDocument.prototype, 'documentElement', { + get: function() { + return this.rootObject || null; + } + }); + + // DOM level 3 + Object.defineProperty(XMLDocument.prototype, 'inputEncoding', { + get: function() { + return null; + } + }); + + Object.defineProperty(XMLDocument.prototype, 'strictErrorChecking', { + get: function() { + return false; + } + }); + + Object.defineProperty(XMLDocument.prototype, 'xmlEncoding', { + get: function() { + if (this.children.length !== 0 && this.children[0].type === NodeType.Declaration) { + return this.children[0].encoding; + } else { + return null; + } + } + }); + + Object.defineProperty(XMLDocument.prototype, 'xmlStandalone', { + get: function() { + if (this.children.length !== 0 && this.children[0].type === NodeType.Declaration) { + return this.children[0].standalone === 'yes'; + } else { + return false; + } + } + }); + + Object.defineProperty(XMLDocument.prototype, 'xmlVersion', { + get: function() { + if (this.children.length !== 0 && this.children[0].type === NodeType.Declaration) { + return this.children[0].version; + } else { + return "1.0"; + } + } + }); + + // DOM level 4 + Object.defineProperty(XMLDocument.prototype, 'URL', { + get: function() { + return this.documentURI; + } + }); + + Object.defineProperty(XMLDocument.prototype, 'origin', { + get: function() { + return null; + } + }); + + Object.defineProperty(XMLDocument.prototype, 'compatMode', { + get: function() { + return null; + } + }); + + Object.defineProperty(XMLDocument.prototype, 'characterSet', { + get: function() { + return null; + } + }); + + Object.defineProperty(XMLDocument.prototype, 'contentType', { + get: function() { + return null; + } + }); + + return XMLDocument; + + }).call(this); + +}).call(this); + +},{"./NodeType":5,"./Utility":6,"./XMLDOMConfiguration":12,"./XMLDOMImplementation":14,"./XMLNode":27,"./XMLStringWriter":32,"./XMLStringifier":33}],23:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, WriterState, XMLAttribute, XMLCData, XMLComment, XMLDTDAttList, XMLDTDElement, XMLDTDEntity, XMLDTDNotation, XMLDeclaration, XMLDocType, XMLDocument, XMLDocumentCB, XMLElement, XMLProcessingInstruction, XMLRaw, XMLStringWriter, XMLStringifier, XMLText, getValue, isFunction, isObject, isPlainObject, + hasProp = {}.hasOwnProperty; + + ({isObject, isFunction, isPlainObject, getValue} = require('./Utility')); + + NodeType = require('./NodeType'); + + XMLDocument = require('./XMLDocument'); + + XMLElement = require('./XMLElement'); + + XMLCData = require('./XMLCData'); + + XMLComment = require('./XMLComment'); + + XMLRaw = require('./XMLRaw'); + + XMLText = require('./XMLText'); + + XMLProcessingInstruction = require('./XMLProcessingInstruction'); + + XMLDeclaration = require('./XMLDeclaration'); + + XMLDocType = require('./XMLDocType'); + + XMLDTDAttList = require('./XMLDTDAttList'); + + XMLDTDEntity = require('./XMLDTDEntity'); + + XMLDTDElement = require('./XMLDTDElement'); + + XMLDTDNotation = require('./XMLDTDNotation'); + + XMLAttribute = require('./XMLAttribute'); + + XMLStringifier = require('./XMLStringifier'); + + XMLStringWriter = require('./XMLStringWriter'); + + WriterState = require('./WriterState'); + + // Represents an XML builder + module.exports = XMLDocumentCB = class XMLDocumentCB { + // Initializes a new instance of `XMLDocumentCB` + + // `options.keepNullNodes` whether nodes with null values will be kept + // or ignored: true or false + // `options.keepNullAttributes` whether attributes with null values will be + // kept or ignored: true or false + // `options.ignoreDecorators` whether decorator strings will be ignored when + // converting JS objects: true or false + // `options.separateArrayItems` whether array items are created as separate + // nodes when passed as an object value: true or false + // `options.noDoubleEncoding` whether existing html entities are encoded: + // true or false + // `options.stringify` a set of functions to use for converting values to + // strings + // `options.writer` the default XML writer to use for converting nodes to + // string. If the default writer is not set, the built-in XMLStringWriter + // will be used instead. + + // `onData` the function to be called when a new chunk of XML is output. The + // string containing the XML chunk is passed to `onData` as its first + // argument, and the current indentation level as its second argument. + // `onEnd` the function to be called when the XML document is completed with + // `end`. `onEnd` does not receive any arguments. + constructor(options, onData, onEnd) { + var writerOptions; + this.name = "?xml"; + this.type = NodeType.Document; + options || (options = {}); + writerOptions = {}; + if (!options.writer) { + options.writer = new XMLStringWriter(); + } else if (isPlainObject(options.writer)) { + writerOptions = options.writer; + options.writer = new XMLStringWriter(); + } + this.options = options; + this.writer = options.writer; + this.writerOptions = this.writer.filterOptions(writerOptions); + this.stringify = new XMLStringifier(options); + this.onDataCallback = onData || function() {}; + this.onEndCallback = onEnd || function() {}; + this.currentNode = null; + this.currentLevel = -1; + this.openTags = {}; + this.documentStarted = false; + this.documentCompleted = false; + this.root = null; + } + + // Creates a child element node from the given XMLNode + + // `node` the child node + createChildNode(node) { + var att, attName, attributes, child, i, len, ref, ref1; + switch (node.type) { + case NodeType.CData: + this.cdata(node.value); + break; + case NodeType.Comment: + this.comment(node.value); + break; + case NodeType.Element: + attributes = {}; + ref = node.attribs; + for (attName in ref) { + if (!hasProp.call(ref, attName)) continue; + att = ref[attName]; + attributes[attName] = att.value; + } + this.node(node.name, attributes); + break; + case NodeType.Dummy: + this.dummy(); + break; + case NodeType.Raw: + this.raw(node.value); + break; + case NodeType.Text: + this.text(node.value); + break; + case NodeType.ProcessingInstruction: + this.instruction(node.target, node.value); + break; + default: + throw new Error("This XML node type is not supported in a JS object: " + node.constructor.name); + } + ref1 = node.children; + // write child nodes recursively + for (i = 0, len = ref1.length; i < len; i++) { + child = ref1[i]; + this.createChildNode(child); + if (child.type === NodeType.Element) { + this.up(); + } + } + return this; + } + + // Creates a dummy node + + dummy() { + // no-op, just return this + return this; + } + + // Creates a node + + // `name` name of the node + // `attributes` an object containing name/value pairs of attributes + // `text` element text + node(name, attributes, text) { + if (name == null) { + throw new Error("Missing node name."); + } + if (this.root && this.currentLevel === -1) { + throw new Error("Document can only have one root node. " + this.debugInfo(name)); + } + this.openCurrent(); + name = getValue(name); + if (attributes == null) { + attributes = {}; + } + attributes = getValue(attributes); + // swap argument order: text <-> attributes + if (!isObject(attributes)) { + [text, attributes] = [attributes, text]; + } + this.currentNode = new XMLElement(this, name, attributes); + this.currentNode.children = false; + this.currentLevel++; + this.openTags[this.currentLevel] = this.currentNode; + if (text != null) { + this.text(text); + } + return this; + } + + // Creates a child element node or an element type declaration when called + // inside the DTD + + // `name` name of the node + // `attributes` an object containing name/value pairs of attributes + // `text` element text + element(name, attributes, text) { + var child, i, len, oldValidationFlag, ref, root; + if (this.currentNode && this.currentNode.type === NodeType.DocType) { + this.dtdElement(...arguments); + } else { + if (Array.isArray(name) || isObject(name) || isFunction(name)) { + oldValidationFlag = this.options.noValidation; + this.options.noValidation = true; + root = new XMLDocument(this.options).element('TEMP_ROOT'); + root.element(name); + this.options.noValidation = oldValidationFlag; + ref = root.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + this.createChildNode(child); + if (child.type === NodeType.Element) { + this.up(); + } + } + } else { + this.node(name, attributes, text); + } + } + return this; + } + + // Adds or modifies an attribute + + // `name` attribute name + // `value` attribute value + attribute(name, value) { + var attName, attValue; + if (!this.currentNode || this.currentNode.children) { + throw new Error("att() can only be used immediately after an ele() call in callback mode. " + this.debugInfo(name)); + } + if (name != null) { + name = getValue(name); + } + if (isObject(name)) { // expand if object + for (attName in name) { + if (!hasProp.call(name, attName)) continue; + attValue = name[attName]; + this.attribute(attName, attValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + if (this.options.keepNullAttributes && (value == null)) { + this.currentNode.attribs[name] = new XMLAttribute(this, name, ""); + } else if (value != null) { + this.currentNode.attribs[name] = new XMLAttribute(this, name, value); + } + } + return this; + } + + // Creates a text node + + // `value` element text + text(value) { + var node; + this.openCurrent(); + node = new XMLText(this, value); + this.onData(this.writer.text(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Creates a CDATA node + + // `value` element text without CDATA delimiters + cdata(value) { + var node; + this.openCurrent(); + node = new XMLCData(this, value); + this.onData(this.writer.cdata(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Creates a comment node + + // `value` comment text + comment(value) { + var node; + this.openCurrent(); + node = new XMLComment(this, value); + this.onData(this.writer.comment(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Adds unescaped raw text + + // `value` text + raw(value) { + var node; + this.openCurrent(); + node = new XMLRaw(this, value); + this.onData(this.writer.raw(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Adds a processing instruction + + // `target` instruction target + // `value` instruction value + instruction(target, value) { + var i, insTarget, insValue, len, node; + this.openCurrent(); + if (target != null) { + target = getValue(target); + } + if (value != null) { + value = getValue(value); + } + if (Array.isArray(target)) { // expand if array + for (i = 0, len = target.length; i < len; i++) { + insTarget = target[i]; + this.instruction(insTarget); + } + } else if (isObject(target)) { // expand if object + for (insTarget in target) { + if (!hasProp.call(target, insTarget)) continue; + insValue = target[insTarget]; + this.instruction(insTarget, insValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + node = new XMLProcessingInstruction(this, target, value); + this.onData(this.writer.processingInstruction(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + } + return this; + } + + // Creates the xml declaration + + // `version` A version number string, e.g. 1.0 + // `encoding` Encoding declaration, e.g. UTF-8 + // `standalone` standalone document declaration: true or false + declaration(version, encoding, standalone) { + var node; + this.openCurrent(); + if (this.documentStarted) { + throw new Error("declaration() must be the first node."); + } + node = new XMLDeclaration(this, version, encoding, standalone); + this.onData(this.writer.declaration(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Creates the document type declaration + + // `root` the name of the root node + // `pubID` the public identifier of the external subset + // `sysID` the system identifier of the external subset + doctype(root, pubID, sysID) { + this.openCurrent(); + if (root == null) { + throw new Error("Missing root node name."); + } + if (this.root) { + throw new Error("dtd() must come before the root node."); + } + this.currentNode = new XMLDocType(this, pubID, sysID); + this.currentNode.rootNodeName = root; + this.currentNode.children = false; + this.currentLevel++; + this.openTags[this.currentLevel] = this.currentNode; + return this; + } + + // Creates an element type declaration + + // `name` element name + // `value` element content (defaults to #PCDATA) + dtdElement(name, value) { + var node; + this.openCurrent(); + node = new XMLDTDElement(this, name, value); + this.onData(this.writer.dtdElement(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Creates an attribute declaration + + // `elementName` the name of the element containing this attribute + // `attributeName` attribute name + // `attributeType` type of the attribute (defaults to CDATA) + // `defaultValueType` default value type (either #REQUIRED, #IMPLIED, #FIXED or + // #DEFAULT) (defaults to #IMPLIED) + // `defaultValue` default value of the attribute + // (only used for #FIXED or #DEFAULT) + attList(elementName, attributeName, attributeType, defaultValueType, defaultValue) { + var node; + this.openCurrent(); + node = new XMLDTDAttList(this, elementName, attributeName, attributeType, defaultValueType, defaultValue); + this.onData(this.writer.dtdAttList(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Creates a general entity declaration + + // `name` the name of the entity + // `value` internal entity value or an object with external entity details + // `value.pubID` public identifier + // `value.sysID` system identifier + // `value.nData` notation declaration + entity(name, value) { + var node; + this.openCurrent(); + node = new XMLDTDEntity(this, false, name, value); + this.onData(this.writer.dtdEntity(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Creates a parameter entity declaration + + // `name` the name of the entity + // `value` internal entity value or an object with external entity details + // `value.pubID` public identifier + // `value.sysID` system identifier + pEntity(name, value) { + var node; + this.openCurrent(); + node = new XMLDTDEntity(this, true, name, value); + this.onData(this.writer.dtdEntity(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Creates a NOTATION declaration + + // `name` the name of the notation + // `value` an object with external entity details + // `value.pubID` public identifier + // `value.sysID` system identifier + notation(name, value) { + var node; + this.openCurrent(); + node = new XMLDTDNotation(this, name, value); + this.onData(this.writer.dtdNotation(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); + return this; + } + + // Gets the parent node + up() { + if (this.currentLevel < 0) { + throw new Error("The document node has no parent."); + } + if (this.currentNode) { + if (this.currentNode.children) { + this.closeNode(this.currentNode); + } else { + this.openNode(this.currentNode); + } + this.currentNode = null; + } else { + this.closeNode(this.openTags[this.currentLevel]); + } + delete this.openTags[this.currentLevel]; + this.currentLevel--; + return this; + } + + // Ends the document + end() { + while (this.currentLevel >= 0) { + this.up(); + } + return this.onEnd(); + } + + // Opens the current parent node + openCurrent() { + if (this.currentNode) { + this.currentNode.children = true; + return this.openNode(this.currentNode); + } + } + + // Writes the opening tag of the current node or the entire node if it has + // no child nodes + openNode(node) { + var att, chunk, name, ref; + if (!node.isOpen) { + if (!this.root && this.currentLevel === 0 && node.type === NodeType.Element) { + this.root = node; + } + chunk = ''; + if (node.type === NodeType.Element) { + this.writerOptions.state = WriterState.OpenTag; + chunk = this.writer.indent(node, this.writerOptions, this.currentLevel) + '<' + node.name; + ref = node.attribs; + for (name in ref) { + if (!hasProp.call(ref, name)) continue; + att = ref[name]; + chunk += this.writer.attribute(att, this.writerOptions, this.currentLevel); + } + chunk += (node.children ? '>' : '/>') + this.writer.endline(node, this.writerOptions, this.currentLevel); + this.writerOptions.state = WriterState.InsideTag; // if node.type is NodeType.DocType + } else { + this.writerOptions.state = WriterState.OpenTag; + chunk = this.writer.indent(node, this.writerOptions, this.currentLevel) + ''; + } + chunk += this.writer.endline(node, this.writerOptions, this.currentLevel); + } + this.onData(chunk, this.currentLevel); + return node.isOpen = true; + } + } + + // Writes the closing tag of the current node + closeNode(node) { + var chunk; + if (!node.isClosed) { + chunk = ''; + this.writerOptions.state = WriterState.CloseTag; + if (node.type === NodeType.Element) { + chunk = this.writer.indent(node, this.writerOptions, this.currentLevel) + '' + this.writer.endline(node, this.writerOptions, this.currentLevel); // if node.type is NodeType.DocType + } else { + chunk = this.writer.indent(node, this.writerOptions, this.currentLevel) + ']>' + this.writer.endline(node, this.writerOptions, this.currentLevel); + } + this.writerOptions.state = WriterState.None; + this.onData(chunk, this.currentLevel); + return node.isClosed = true; + } + } + + // Called when a new chunk of XML is output + + // `chunk` a string containing the XML chunk + // `level` current indentation level + onData(chunk, level) { + this.documentStarted = true; + return this.onDataCallback(chunk, level + 1); + } + + // Called when the XML document is completed + onEnd() { + this.documentCompleted = true; + return this.onEndCallback(); + } + + // Returns debug string + debugInfo(name) { + if (name == null) { + return ""; + } else { + return "node: <" + name + ">"; + } + } + + // Node aliases + ele() { + return this.element(...arguments); + } + + nod(name, attributes, text) { + return this.node(name, attributes, text); + } + + txt(value) { + return this.text(value); + } + + dat(value) { + return this.cdata(value); + } + + com(value) { + return this.comment(value); + } + + ins(target, value) { + return this.instruction(target, value); + } + + dec(version, encoding, standalone) { + return this.declaration(version, encoding, standalone); + } + + dtd(root, pubID, sysID) { + return this.doctype(root, pubID, sysID); + } + + e(name, attributes, text) { + return this.element(name, attributes, text); + } + + n(name, attributes, text) { + return this.node(name, attributes, text); + } + + t(value) { + return this.text(value); + } + + d(value) { + return this.cdata(value); + } + + c(value) { + return this.comment(value); + } + + r(value) { + return this.raw(value); + } + + i(target, value) { + return this.instruction(target, value); + } + + // Attribute aliases + att() { + if (this.currentNode && this.currentNode.type === NodeType.DocType) { + return this.attList(...arguments); + } else { + return this.attribute(...arguments); + } + } + + a() { + if (this.currentNode && this.currentNode.type === NodeType.DocType) { + return this.attList(...arguments); + } else { + return this.attribute(...arguments); + } + } + + // DTD aliases + // att() and ele() are defined above + ent(name, value) { + return this.entity(name, value); + } + + pent(name, value) { + return this.pEntity(name, value); + } + + not(name, value) { + return this.notation(name, value); + } + + }; + +}).call(this); + +},{"./NodeType":5,"./Utility":6,"./WriterState":7,"./XMLAttribute":8,"./XMLCData":9,"./XMLComment":11,"./XMLDTDAttList":16,"./XMLDTDElement":17,"./XMLDTDEntity":18,"./XMLDTDNotation":19,"./XMLDeclaration":20,"./XMLDocType":21,"./XMLDocument":22,"./XMLElement":25,"./XMLProcessingInstruction":29,"./XMLRaw":30,"./XMLStringWriter":32,"./XMLStringifier":33,"./XMLText":34}],24:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLDummy, XMLNode; + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + // Represents a raw node + module.exports = XMLDummy = class XMLDummy extends XMLNode { + // Initializes a new instance of `XMLDummy` + + // `XMLDummy` is a special node representing a node with + // a null value. Dummy nodes are created while recursively + // building the XML tree. Simply skipping null values doesn't + // work because that would break the recursive chain. + constructor(parent) { + super(parent); + this.type = NodeType.Dummy; + } + + // Creates and returns a deep clone of `this` + clone() { + return Object.create(this); + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return ''; + } + + }; + +}).call(this); + +},{"./NodeType":5,"./XMLNode":27}],25:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLAttribute, XMLElement, XMLNamedNodeMap, XMLNode, getValue, isFunction, isObject, + hasProp = {}.hasOwnProperty; + + ({isObject, isFunction, getValue} = require('./Utility')); + + XMLNode = require('./XMLNode'); + + NodeType = require('./NodeType'); + + XMLAttribute = require('./XMLAttribute'); + + XMLNamedNodeMap = require('./XMLNamedNodeMap'); + + // Represents an element of the XML document + module.exports = XMLElement = (function() { + class XMLElement extends XMLNode { + // Initializes a new instance of `XMLElement` + + // `parent` the parent node + // `name` element name + // `attributes` an object containing name/value pairs of attributes + constructor(parent, name, attributes) { + var child, j, len, ref; + super(parent); + if (name == null) { + throw new Error("Missing element name. " + this.debugInfo()); + } + this.name = this.stringify.name(name); + this.type = NodeType.Element; + this.attribs = {}; + this.schemaTypeInfo = null; + if (attributes != null) { + this.attribute(attributes); + } + // set properties if this is the root node + if (parent.type === NodeType.Document) { + this.isRoot = true; + this.documentObject = parent; + parent.rootObject = this; + // set dtd name + if (parent.children) { + ref = parent.children; + for (j = 0, len = ref.length; j < len; j++) { + child = ref[j]; + if (child.type === NodeType.DocType) { + child.name = this.name; + break; + } + } + } + } + } + + // Creates and returns a deep clone of `this` + + clone() { + var att, attName, clonedSelf, ref; + clonedSelf = Object.create(this); + // remove document element + if (clonedSelf.isRoot) { + clonedSelf.documentObject = null; + } + // clone attributes + clonedSelf.attribs = {}; + ref = this.attribs; + for (attName in ref) { + if (!hasProp.call(ref, attName)) continue; + att = ref[attName]; + clonedSelf.attribs[attName] = att.clone(); + } + // clone child nodes + clonedSelf.children = []; + this.children.forEach(function(child) { + var clonedChild; + clonedChild = child.clone(); + clonedChild.parent = clonedSelf; + return clonedSelf.children.push(clonedChild); + }); + return clonedSelf; + } + + // Adds or modifies an attribute + + // `name` attribute name + // `value` attribute value + attribute(name, value) { + var attName, attValue; + if (name != null) { + name = getValue(name); + } + if (isObject(name)) { // expand if object + for (attName in name) { + if (!hasProp.call(name, attName)) continue; + attValue = name[attName]; + this.attribute(attName, attValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + if (this.options.keepNullAttributes && (value == null)) { + this.attribs[name] = new XMLAttribute(this, name, ""); + } else if (value != null) { + this.attribs[name] = new XMLAttribute(this, name, value); + } + } + return this; + } + + // Removes an attribute + + // `name` attribute name + removeAttribute(name) { + var attName, j, len; + // Also defined in DOM level 1 + // removeAttribute(name) removes an attribute by name. + if (name == null) { + throw new Error("Missing attribute name. " + this.debugInfo()); + } + name = getValue(name); + if (Array.isArray(name)) { // expand if array + for (j = 0, len = name.length; j < len; j++) { + attName = name[j]; + delete this.attribs[attName]; + } + } else { + delete this.attribs[name]; + } + return this; + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + // `options.allowEmpty` do not self close empty element tags + toString(options) { + return this.options.writer.element(this, this.options.writer.filterOptions(options)); + } + + // Aliases + att(name, value) { + return this.attribute(name, value); + } + + a(name, value) { + return this.attribute(name, value); + } + + // DOM Level 1 + getAttribute(name) { + if (this.attribs.hasOwnProperty(name)) { + return this.attribs[name].value; + } else { + return null; + } + } + + setAttribute(name, value) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getAttributeNode(name) { + if (this.attribs.hasOwnProperty(name)) { + return this.attribs[name]; + } else { + return null; + } + } + + setAttributeNode(newAttr) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + removeAttributeNode(oldAttr) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getElementsByTagName(name) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + // DOM Level 2 + getAttributeNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + setAttributeNS(namespaceURI, qualifiedName, value) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + removeAttributeNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getAttributeNodeNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + setAttributeNodeNS(newAttr) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getElementsByTagNameNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + hasAttribute(name) { + return this.attribs.hasOwnProperty(name); + } + + hasAttributeNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + // DOM Level 3 + setIdAttribute(name, isId) { + if (this.attribs.hasOwnProperty(name)) { + return this.attribs[name].isId; + } else { + return isId; + } + } + + setIdAttributeNS(namespaceURI, localName, isId) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + setIdAttributeNode(idAttr, isId) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + // DOM Level 4 + getElementsByTagName(tagname) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getElementsByTagNameNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getElementsByClassName(classNames) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + isEqualNode(node) { + var i, j, ref; + if (!super.isEqualNode(node)) { + return false; + } + if (node.namespaceURI !== this.namespaceURI) { + return false; + } + if (node.prefix !== this.prefix) { + return false; + } + if (node.localName !== this.localName) { + return false; + } + if (node.attribs.length !== this.attribs.length) { + return false; + } + for (i = j = 0, ref = this.attribs.length - 1; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) { + if (!this.attribs[i].isEqualNode(node.attribs[i])) { + return false; + } + } + return true; + } + + }; + + // DOM level 1 + Object.defineProperty(XMLElement.prototype, 'tagName', { + get: function() { + return this.name; + } + }); + + // DOM level 4 + Object.defineProperty(XMLElement.prototype, 'namespaceURI', { + get: function() { + return ''; + } + }); + + Object.defineProperty(XMLElement.prototype, 'prefix', { + get: function() { + return ''; + } + }); + + Object.defineProperty(XMLElement.prototype, 'localName', { + get: function() { + return this.name; + } + }); + + Object.defineProperty(XMLElement.prototype, 'id', { + get: function() { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + }); + + Object.defineProperty(XMLElement.prototype, 'className', { + get: function() { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + }); + + Object.defineProperty(XMLElement.prototype, 'classList', { + get: function() { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + }); + + Object.defineProperty(XMLElement.prototype, 'attributes', { + get: function() { + if (!this.attributeMap || !this.attributeMap.nodes) { + this.attributeMap = new XMLNamedNodeMap(this.attribs); + } + return this.attributeMap; + } + }); + + return XMLElement; + + }).call(this); + +}).call(this); + +},{"./NodeType":5,"./Utility":6,"./XMLAttribute":8,"./XMLNamedNodeMap":26,"./XMLNode":27}],26:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + // Represents a map of nodes accessed by a string key + var XMLNamedNodeMap; + + module.exports = XMLNamedNodeMap = (function() { + class XMLNamedNodeMap { + // Initializes a new instance of `XMLNamedNodeMap` + // This is just a wrapper around an ordinary + // JS object. + + // `nodes` the object containing nodes. + constructor(nodes) { + this.nodes = nodes; + } + + // Creates and returns a deep clone of `this` + + clone() { + // this class should not be cloned since it wraps + // around a given object. The calling function should check + // whether the wrapped object is null and supply a new object + // (from the clone). + return this.nodes = null; + } + + // DOM Level 1 + getNamedItem(name) { + return this.nodes[name]; + } + + setNamedItem(node) { + var oldNode; + oldNode = this.nodes[node.nodeName]; + this.nodes[node.nodeName] = node; + return oldNode || null; + } + + removeNamedItem(name) { + var oldNode; + oldNode = this.nodes[name]; + delete this.nodes[name]; + return oldNode || null; + } + + item(index) { + return this.nodes[Object.keys(this.nodes)[index]] || null; + } + + // DOM level 2 functions to be implemented later + getNamedItemNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented."); + } + + setNamedItemNS(node) { + throw new Error("This DOM method is not implemented."); + } + + removeNamedItemNS(namespaceURI, localName) { + throw new Error("This DOM method is not implemented."); + } + + }; + + + // DOM level 1 + Object.defineProperty(XMLNamedNodeMap.prototype, 'length', { + get: function() { + return Object.keys(this.nodes).length || 0; + } + }); + + return XMLNamedNodeMap; + + }).call(this); + +}).call(this); + +},{}],27:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var DocumentPosition, NodeType, XMLCData, XMLComment, XMLDeclaration, XMLDocType, XMLDummy, XMLElement, XMLNamedNodeMap, XMLNode, XMLNodeList, XMLProcessingInstruction, XMLRaw, XMLText, getValue, isEmpty, isFunction, isObject, + hasProp = {}.hasOwnProperty, + splice = [].splice; + + ({isObject, isFunction, isEmpty, getValue} = require('./Utility')); + + XMLElement = null; + + XMLCData = null; + + XMLComment = null; + + XMLDeclaration = null; + + XMLDocType = null; + + XMLRaw = null; + + XMLText = null; + + XMLProcessingInstruction = null; + + XMLDummy = null; + + NodeType = null; + + XMLNodeList = null; + + XMLNamedNodeMap = null; + + DocumentPosition = null; + + // Represents a generic XMl element + module.exports = XMLNode = (function() { + class XMLNode { + // Initializes a new instance of `XMLNode` + + // `parent` the parent node + constructor(parent1) { + this.parent = parent1; + if (this.parent) { + this.options = this.parent.options; + this.stringify = this.parent.stringify; + } + this.value = null; + this.children = []; + this.baseURI = null; + // first execution, load dependencies that are otherwise + // circular (so we can't load them at the top) + if (!XMLElement) { + XMLElement = require('./XMLElement'); + XMLCData = require('./XMLCData'); + XMLComment = require('./XMLComment'); + XMLDeclaration = require('./XMLDeclaration'); + XMLDocType = require('./XMLDocType'); + XMLRaw = require('./XMLRaw'); + XMLText = require('./XMLText'); + XMLProcessingInstruction = require('./XMLProcessingInstruction'); + XMLDummy = require('./XMLDummy'); + NodeType = require('./NodeType'); + XMLNodeList = require('./XMLNodeList'); + XMLNamedNodeMap = require('./XMLNamedNodeMap'); + DocumentPosition = require('./DocumentPosition'); + } + } + + + // Sets the parent node of this node and its children recursively + + // `parent` the parent node + setParent(parent) { + var child, j, len, ref1, results; + this.parent = parent; + if (parent) { + this.options = parent.options; + this.stringify = parent.stringify; + } + ref1 = this.children; + results = []; + for (j = 0, len = ref1.length; j < len; j++) { + child = ref1[j]; + results.push(child.setParent(this)); + } + return results; + } + + // Creates a child element node + + // `name` node name or an object describing the XML tree + // `attributes` an object containing name/value pairs of attributes + // `text` element text + element(name, attributes, text) { + var childNode, item, j, k, key, lastChild, len, len1, val; + lastChild = null; + if (attributes === null && (text == null)) { + [attributes, text] = [{}, null]; + } + if (attributes == null) { + attributes = {}; + } + attributes = getValue(attributes); + // swap argument order: text <-> attributes + if (!isObject(attributes)) { + [text, attributes] = [attributes, text]; + } + if (name != null) { + name = getValue(name); + } + // expand if array + if (Array.isArray(name)) { + for (j = 0, len = name.length; j < len; j++) { + item = name[j]; + lastChild = this.element(item); + } + // evaluate if function + } else if (isFunction(name)) { + lastChild = this.element(name.apply()); + // expand if object + } else if (isObject(name)) { + for (key in name) { + if (!hasProp.call(name, key)) continue; + val = name[key]; + if (isFunction(val)) { + // evaluate if function + val = val.apply(); + } + // assign attributes + if (!this.options.ignoreDecorators && this.stringify.convertAttKey && key.indexOf(this.stringify.convertAttKey) === 0) { + lastChild = this.attribute(key.substr(this.stringify.convertAttKey.length), val); + // skip empty arrays + } else if (!this.options.separateArrayItems && Array.isArray(val) && isEmpty(val)) { + lastChild = this.dummy(); + // empty objects produce one node + } else if (isObject(val) && isEmpty(val)) { + lastChild = this.element(key); + // skip null and undefined nodes + } else if (!this.options.keepNullNodes && (val == null)) { + lastChild = this.dummy(); + + // expand list by creating child nodes + } else if (!this.options.separateArrayItems && Array.isArray(val)) { + for (k = 0, len1 = val.length; k < len1; k++) { + item = val[k]; + childNode = {}; + childNode[key] = item; + lastChild = this.element(childNode); + } + + // expand child nodes under parent + } else if (isObject(val)) { + // if the key is #text expand child nodes under this node to support mixed content + if (!this.options.ignoreDecorators && this.stringify.convertTextKey && key.indexOf(this.stringify.convertTextKey) === 0) { + lastChild = this.element(val); + } else { + lastChild = this.element(key); + lastChild.element(val); + } + } else { + + // text node + lastChild = this.element(key, val); + } + } + // skip null nodes + } else if (!this.options.keepNullNodes && text === null) { + lastChild = this.dummy(); + } else { + // text node + if (!this.options.ignoreDecorators && this.stringify.convertTextKey && name.indexOf(this.stringify.convertTextKey) === 0) { + lastChild = this.text(text); + // cdata node + } else if (!this.options.ignoreDecorators && this.stringify.convertCDataKey && name.indexOf(this.stringify.convertCDataKey) === 0) { + lastChild = this.cdata(text); + // comment node + } else if (!this.options.ignoreDecorators && this.stringify.convertCommentKey && name.indexOf(this.stringify.convertCommentKey) === 0) { + lastChild = this.comment(text); + // raw text node + } else if (!this.options.ignoreDecorators && this.stringify.convertRawKey && name.indexOf(this.stringify.convertRawKey) === 0) { + lastChild = this.raw(text); + // processing instruction + } else if (!this.options.ignoreDecorators && this.stringify.convertPIKey && name.indexOf(this.stringify.convertPIKey) === 0) { + lastChild = this.instruction(name.substr(this.stringify.convertPIKey.length), text); + } else { + // element node + lastChild = this.node(name, attributes, text); + } + } + if (lastChild == null) { + throw new Error("Could not create any elements with: " + name + ". " + this.debugInfo()); + } + return lastChild; + } + + // Creates a child element node before the current node + + // `name` node name or an object describing the XML tree + // `attributes` an object containing name/value pairs of attributes + // `text` element text + insertBefore(name, attributes, text) { + var child, i, newChild, refChild, removed; + // DOM level 1 + // insertBefore(newChild, refChild) inserts the child node newChild before refChild + if (name != null ? name.type : void 0) { + newChild = name; + refChild = attributes; + newChild.setParent(this); + if (refChild) { + // temporarily remove children starting *with* refChild + i = children.indexOf(refChild); + removed = children.splice(i); + + // add the new child + children.push(newChild); + + // add back removed children after new child + Array.prototype.push.apply(children, removed); + } else { + children.push(newChild); + } + return newChild; + } else { + if (this.isRoot) { + throw new Error("Cannot insert elements at root level. " + this.debugInfo(name)); + } + + // temporarily remove children starting *with* this + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i); + + // add the new child + child = this.parent.element(name, attributes, text); + + // add back removed children after new child + Array.prototype.push.apply(this.parent.children, removed); + return child; + } + } + + // Creates a child element node after the current node + + // `name` node name or an object describing the XML tree + // `attributes` an object containing name/value pairs of attributes + // `text` element text + insertAfter(name, attributes, text) { + var child, i, removed; + if (this.isRoot) { + throw new Error("Cannot insert elements at root level. " + this.debugInfo(name)); + } + + // temporarily remove children starting *after* this + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i + 1); + + // add the new child + child = this.parent.element(name, attributes, text); + + // add back removed children after new child + Array.prototype.push.apply(this.parent.children, removed); + return child; + } + + // Deletes a child element node + + remove() { + var i, ref1; + if (this.isRoot) { + throw new Error("Cannot remove the root element. " + this.debugInfo()); + } + i = this.parent.children.indexOf(this); + splice.apply(this.parent.children, [i, i - i + 1].concat(ref1 = [])), ref1; + return this.parent; + } + + // Creates a node + + // `name` name of the node + // `attributes` an object containing name/value pairs of attributes + // `text` element text + node(name, attributes, text) { + var child; + if (name != null) { + name = getValue(name); + } + attributes || (attributes = {}); + attributes = getValue(attributes); + // swap argument order: text <-> attributes + if (!isObject(attributes)) { + [text, attributes] = [attributes, text]; + } + child = new XMLElement(this, name, attributes); + if (text != null) { + child.text(text); + } + this.children.push(child); + return child; + } + + // Creates a text node + + // `value` element text + text(value) { + var child; + if (isObject(value)) { + this.element(value); + } + child = new XMLText(this, value); + this.children.push(child); + return this; + } + + // Creates a CDATA node + + // `value` element text without CDATA delimiters + cdata(value) { + var child; + child = new XMLCData(this, value); + this.children.push(child); + return this; + } + + // Creates a comment node + + // `value` comment text + comment(value) { + var child; + child = new XMLComment(this, value); + this.children.push(child); + return this; + } + + // Creates a comment node before the current node + + // `value` comment text + commentBefore(value) { + var child, i, removed; + // temporarily remove children starting *with* this + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i); + // add the new child + child = this.parent.comment(value); + // add back removed children after new child + Array.prototype.push.apply(this.parent.children, removed); + return this; + } + + // Creates a comment node after the current node + + // `value` comment text + commentAfter(value) { + var child, i, removed; + // temporarily remove children starting *after* this + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i + 1); + // add the new child + child = this.parent.comment(value); + // add back removed children after new child + Array.prototype.push.apply(this.parent.children, removed); + return this; + } + + // Adds unescaped raw text + + // `value` text + raw(value) { + var child; + child = new XMLRaw(this, value); + this.children.push(child); + return this; + } + + // Adds a dummy node + dummy() { + var child; + child = new XMLDummy(this); + // Normally when a new node is created it is added to the child node collection. + // However, dummy nodes are never added to the XML tree. They are created while + // converting JS objects to XML nodes in order not to break the recursive function + // chain. They can be thought of as invisible nodes. They can be traversed through + // by using prev(), next(), up(), etc. functions but they do not exists in the tree. + + // @children.push child + return child; + } + + // Adds a processing instruction + + // `target` instruction target + // `value` instruction value + instruction(target, value) { + var insTarget, insValue, instruction, j, len; + if (target != null) { + target = getValue(target); + } + if (value != null) { + value = getValue(value); + } + if (Array.isArray(target)) { // expand if array + for (j = 0, len = target.length; j < len; j++) { + insTarget = target[j]; + this.instruction(insTarget); + } + } else if (isObject(target)) { // expand if object + for (insTarget in target) { + if (!hasProp.call(target, insTarget)) continue; + insValue = target[insTarget]; + this.instruction(insTarget, insValue); + } + } else { + if (isFunction(value)) { + value = value.apply(); + } + instruction = new XMLProcessingInstruction(this, target, value); + this.children.push(instruction); + } + return this; + } + + // Creates a processing instruction node before the current node + + // `target` instruction target + // `value` instruction value + instructionBefore(target, value) { + var child, i, removed; + // temporarily remove children starting *with* this + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i); + // add the new child + child = this.parent.instruction(target, value); + // add back removed children after new child + Array.prototype.push.apply(this.parent.children, removed); + return this; + } + + // Creates a processing instruction node after the current node + + // `target` instruction target + // `value` instruction value + instructionAfter(target, value) { + var child, i, removed; + // temporarily remove children starting *after* this + i = this.parent.children.indexOf(this); + removed = this.parent.children.splice(i + 1); + // add the new child + child = this.parent.instruction(target, value); + // add back removed children after new child + Array.prototype.push.apply(this.parent.children, removed); + return this; + } + + // Creates the xml declaration + + // `version` A version number string, e.g. 1.0 + // `encoding` Encoding declaration, e.g. UTF-8 + // `standalone` standalone document declaration: true or false + declaration(version, encoding, standalone) { + var doc, xmldec; + doc = this.document(); + xmldec = new XMLDeclaration(doc, version, encoding, standalone); + // Replace XML declaration if exists, otherwise insert at top + if (doc.children.length === 0) { + doc.children.unshift(xmldec); + } else if (doc.children[0].type === NodeType.Declaration) { + doc.children[0] = xmldec; + } else { + doc.children.unshift(xmldec); + } + return doc.root() || doc; + } + + // Creates the document type declaration + + // `pubID` the public identifier of the external subset + // `sysID` the system identifier of the external subset + dtd(pubID, sysID) { + var child, doc, doctype, i, j, k, len, len1, ref1, ref2; + doc = this.document(); + doctype = new XMLDocType(doc, pubID, sysID); + ref1 = doc.children; + // Replace DTD if exists + for (i = j = 0, len = ref1.length; j < len; i = ++j) { + child = ref1[i]; + if (child.type === NodeType.DocType) { + doc.children[i] = doctype; + return doctype; + } + } + ref2 = doc.children; + // insert before root node if the root node exists + for (i = k = 0, len1 = ref2.length; k < len1; i = ++k) { + child = ref2[i]; + if (child.isRoot) { + doc.children.splice(i, 0, doctype); + return doctype; + } + } + // otherwise append to end + doc.children.push(doctype); + return doctype; + } + + // Gets the parent node + up() { + if (this.isRoot) { + throw new Error("The root node has no parent. Use doc() if you need to get the document object."); + } + return this.parent; + } + + // Gets the root node + root() { + var node; + node = this; + while (node) { + if (node.type === NodeType.Document) { + return node.rootObject; + } else if (node.isRoot) { + return node; + } else { + node = node.parent; + } + } + } + + // Gets the node representing the XML document + document() { + var node; + node = this; + while (node) { + if (node.type === NodeType.Document) { + return node; + } else { + node = node.parent; + } + } + } + + // Ends the document and converts string + end(options) { + return this.document().end(options); + } + + // Gets the previous node + prev() { + var i; + i = this.parent.children.indexOf(this); + if (i < 1) { + throw new Error("Already at the first node. " + this.debugInfo()); + } + return this.parent.children[i - 1]; + } + + // Gets the next node + next() { + var i; + i = this.parent.children.indexOf(this); + if (i === -1 || i === this.parent.children.length - 1) { + throw new Error("Already at the last node. " + this.debugInfo()); + } + return this.parent.children[i + 1]; + } + + // Imports cloned root from another XML document + + // `doc` the XML document to insert nodes from + importDocument(doc) { + var child, clonedRoot, j, len, ref1; + clonedRoot = doc.root().clone(); + clonedRoot.parent = this; + clonedRoot.isRoot = false; + this.children.push(clonedRoot); + // set properties if imported element becomes the root node + if (this.type === NodeType.Document) { + clonedRoot.isRoot = true; + clonedRoot.documentObject = this; + this.rootObject = clonedRoot; + // set dtd name + if (this.children) { + ref1 = this.children; + for (j = 0, len = ref1.length; j < len; j++) { + child = ref1[j]; + if (child.type === NodeType.DocType) { + child.name = clonedRoot.name; + break; + } + } + } + } + return this; + } + + + // Returns debug string for this node + debugInfo(name) { + var ref1, ref2; + name = name || this.name; + if ((name == null) && !((ref1 = this.parent) != null ? ref1.name : void 0)) { + return ""; + } else if (name == null) { + return "parent: <" + this.parent.name + ">"; + } else if (!((ref2 = this.parent) != null ? ref2.name : void 0)) { + return "node: <" + name + ">"; + } else { + return "node: <" + name + ">, parent: <" + this.parent.name + ">"; + } + } + + // Aliases + ele(name, attributes, text) { + return this.element(name, attributes, text); + } + + nod(name, attributes, text) { + return this.node(name, attributes, text); + } + + txt(value) { + return this.text(value); + } + + dat(value) { + return this.cdata(value); + } + + com(value) { + return this.comment(value); + } + + ins(target, value) { + return this.instruction(target, value); + } + + doc() { + return this.document(); + } + + dec(version, encoding, standalone) { + return this.declaration(version, encoding, standalone); + } + + e(name, attributes, text) { + return this.element(name, attributes, text); + } + + n(name, attributes, text) { + return this.node(name, attributes, text); + } + + t(value) { + return this.text(value); + } + + d(value) { + return this.cdata(value); + } + + c(value) { + return this.comment(value); + } + + r(value) { + return this.raw(value); + } + + i(target, value) { + return this.instruction(target, value); + } + + u() { + return this.up(); + } + + // can be deprecated in a future release + importXMLBuilder(doc) { + return this.importDocument(doc); + } + + // Adds or modifies an attribute. + + // `name` attribute name + // `value` attribute value + attribute(name, value) { + throw new Error("attribute() applies to element nodes only."); + } + + att(name, value) { + return this.attribute(name, value); + } + + a(name, value) { + return this.attribute(name, value); + } + + // Removes an attribute + + // `name` attribute name + removeAttribute(name) { + throw new Error("attribute() applies to element nodes only."); + } + + // DOM level 1 functions to be implemented later + replaceChild(newChild, oldChild) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + removeChild(oldChild) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + appendChild(newChild) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + hasChildNodes() { + return this.children.length !== 0; + } + + cloneNode(deep) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + normalize() { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + // DOM level 2 + isSupported(feature, version) { + return true; + } + + hasAttributes() { + return this.attribs.length !== 0; + } + + // DOM level 3 functions to be implemented later + compareDocumentPosition(other) { + var ref, res; + ref = this; + if (ref === other) { + return 0; + } else if (this.document() !== other.document()) { + res = DocumentPosition.Disconnected | DocumentPosition.ImplementationSpecific; + if (Math.random() < 0.5) { + res |= DocumentPosition.Preceding; + } else { + res |= DocumentPosition.Following; + } + return res; + } else if (ref.isAncestor(other)) { + return DocumentPosition.Contains | DocumentPosition.Preceding; + } else if (ref.isDescendant(other)) { + return DocumentPosition.Contains | DocumentPosition.Following; + } else if (ref.isPreceding(other)) { + return DocumentPosition.Preceding; + } else { + return DocumentPosition.Following; + } + } + + isSameNode(other) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + lookupPrefix(namespaceURI) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + isDefaultNamespace(namespaceURI) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + lookupNamespaceURI(prefix) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + isEqualNode(node) { + var i, j, ref1; + if (node.nodeType !== this.nodeType) { + return false; + } + if (node.children.length !== this.children.length) { + return false; + } + for (i = j = 0, ref1 = this.children.length - 1; (0 <= ref1 ? j <= ref1 : j >= ref1); i = 0 <= ref1 ? ++j : --j) { + if (!this.children[i].isEqualNode(node.children[i])) { + return false; + } + } + return true; + } + + getFeature(feature, version) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + setUserData(key, data, handler) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + getUserData(key) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + + // Returns true if other is an inclusive descendant of node, + // and false otherwise. + contains(other) { + if (!other) { + return false; + } + return other === this || this.isDescendant(other); + } + + // An object A is called a descendant of an object B, if either A is + // a child of B or A is a child of an object C that is a descendant of B. + isDescendant(node) { + var child, isDescendantChild, j, len, ref1; + ref1 = this.children; + for (j = 0, len = ref1.length; j < len; j++) { + child = ref1[j]; + if (node === child) { + return true; + } + isDescendantChild = child.isDescendant(node); + if (isDescendantChild) { + return true; + } + } + return false; + } + + // An object A is called an ancestor of an object B if and only if + // B is a descendant of A. + isAncestor(node) { + return node.isDescendant(this); + } + + // An object A is preceding an object B if A and B are in the + // same tree and A comes before B in tree order. + isPreceding(node) { + var nodePos, thisPos; + nodePos = this.treePosition(node); + thisPos = this.treePosition(this); + if (nodePos === -1 || thisPos === -1) { + return false; + } else { + return nodePos < thisPos; + } + } + + // An object A is folllowing an object B if A and B are in the + // same tree and A comes after B in tree order. + isFollowing(node) { + var nodePos, thisPos; + nodePos = this.treePosition(node); + thisPos = this.treePosition(this); + if (nodePos === -1 || thisPos === -1) { + return false; + } else { + return nodePos > thisPos; + } + } + + // Returns the preorder position of the given node in the tree, or -1 + // if the node is not in the tree. + treePosition(node) { + var found, pos; + pos = 0; + found = false; + this.foreachTreeNode(this.document(), function(childNode) { + pos++; + if (!found && childNode === node) { + return found = true; + } + }); + if (found) { + return pos; + } else { + return -1; + } + } + + + // Depth-first preorder traversal through the XML tree + foreachTreeNode(node, func) { + var child, j, len, ref1, res; + node || (node = this.document()); + ref1 = node.children; + for (j = 0, len = ref1.length; j < len; j++) { + child = ref1[j]; + if (res = func(child)) { + return res; + } else { + res = this.foreachTreeNode(child, func); + if (res) { + return res; + } + } + } + } + + }; + + // DOM level 1 + Object.defineProperty(XMLNode.prototype, 'nodeName', { + get: function() { + return this.name; + } + }); + + Object.defineProperty(XMLNode.prototype, 'nodeType', { + get: function() { + return this.type; + } + }); + + Object.defineProperty(XMLNode.prototype, 'nodeValue', { + get: function() { + return this.value; + } + }); + + Object.defineProperty(XMLNode.prototype, 'parentNode', { + get: function() { + return this.parent; + } + }); + + Object.defineProperty(XMLNode.prototype, 'childNodes', { + get: function() { + if (!this.childNodeList || !this.childNodeList.nodes) { + this.childNodeList = new XMLNodeList(this.children); + } + return this.childNodeList; + } + }); + + Object.defineProperty(XMLNode.prototype, 'firstChild', { + get: function() { + return this.children[0] || null; + } + }); + + Object.defineProperty(XMLNode.prototype, 'lastChild', { + get: function() { + return this.children[this.children.length - 1] || null; + } + }); + + Object.defineProperty(XMLNode.prototype, 'previousSibling', { + get: function() { + var i; + i = this.parent.children.indexOf(this); + return this.parent.children[i - 1] || null; + } + }); + + Object.defineProperty(XMLNode.prototype, 'nextSibling', { + get: function() { + var i; + i = this.parent.children.indexOf(this); + return this.parent.children[i + 1] || null; + } + }); + + Object.defineProperty(XMLNode.prototype, 'ownerDocument', { + get: function() { + return this.document() || null; + } + }); + + // DOM level 3 + Object.defineProperty(XMLNode.prototype, 'textContent', { + get: function() { + var child, j, len, ref1, str; + if (this.nodeType === NodeType.Element || this.nodeType === NodeType.DocumentFragment) { + str = ''; + ref1 = this.children; + for (j = 0, len = ref1.length; j < len; j++) { + child = ref1[j]; + if (child.textContent) { + str += child.textContent; + } + } + return str; + } else { + return null; + } + }, + set: function(value) { + throw new Error("This DOM method is not implemented." + this.debugInfo()); + } + }); + + return XMLNode; + + }).call(this); + +}).call(this); + +},{"./DocumentPosition":4,"./NodeType":5,"./Utility":6,"./XMLCData":9,"./XMLComment":11,"./XMLDeclaration":20,"./XMLDocType":21,"./XMLDummy":24,"./XMLElement":25,"./XMLNamedNodeMap":26,"./XMLNodeList":28,"./XMLProcessingInstruction":29,"./XMLRaw":30,"./XMLText":34}],28:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + // Represents a list of nodes + var XMLNodeList; + + module.exports = XMLNodeList = (function() { + class XMLNodeList { + // Initializes a new instance of `XMLNodeList` + // This is just a wrapper around an ordinary + // JS array. + + // `nodes` the array containing nodes. + constructor(nodes) { + this.nodes = nodes; + } + + // Creates and returns a deep clone of `this` + + clone() { + // this class should not be cloned since it wraps + // around a given array. The calling function should check + // whether the wrapped array is null and supply a new array + // (from the clone). + return this.nodes = null; + } + + // DOM Level 1 + item(index) { + return this.nodes[index] || null; + } + + }; + + // DOM level 1 + Object.defineProperty(XMLNodeList.prototype, 'length', { + get: function() { + return this.nodes.length || 0; + } + }); + + return XMLNodeList; + + }).call(this); + +}).call(this); + +},{}],29:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLCharacterData, XMLProcessingInstruction; + + NodeType = require('./NodeType'); + + XMLCharacterData = require('./XMLCharacterData'); + + // Represents a processing instruction + module.exports = XMLProcessingInstruction = class XMLProcessingInstruction extends XMLCharacterData { + // Initializes a new instance of `XMLProcessingInstruction` + + // `parent` the parent node + // `target` instruction target + // `value` instruction value + constructor(parent, target, value) { + super(parent); + if (target == null) { + throw new Error("Missing instruction target. " + this.debugInfo()); + } + this.type = NodeType.ProcessingInstruction; + this.target = this.stringify.insTarget(target); + this.name = this.target; + if (value) { + this.value = this.stringify.insValue(value); + } + } + + // Creates and returns a deep clone of `this` + clone() { + return Object.create(this); + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.processingInstruction(this, this.options.writer.filterOptions(options)); + } + + isEqualNode(node) { + if (!super.isEqualNode(node)) { + return false; + } + if (node.target !== this.target) { + return false; + } + return true; + } + + }; + +}).call(this); + +},{"./NodeType":5,"./XMLCharacterData":10}],30:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, XMLNode, XMLRaw; + + NodeType = require('./NodeType'); + + XMLNode = require('./XMLNode'); + + // Represents a raw node + module.exports = XMLRaw = class XMLRaw extends XMLNode { + // Initializes a new instance of `XMLRaw` + + // `text` raw text + constructor(parent, text) { + super(parent); + if (text == null) { + throw new Error("Missing raw text. " + this.debugInfo()); + } + this.type = NodeType.Raw; + this.value = this.stringify.raw(text); + } + + // Creates and returns a deep clone of `this` + clone() { + return Object.create(this); + } + + // Converts the XML fragment to string + + // `options.pretty` pretty prints the result + // `options.indent` indentation for pretty print + // `options.offset` how many indentations to add to every line for pretty print + // `options.newline` newline sequence for pretty print + toString(options) { + return this.options.writer.raw(this, this.options.writer.filterOptions(options)); + } + + }; + +}).call(this); + +},{"./NodeType":5,"./XMLNode":27}],31:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, WriterState, XMLStreamWriter, XMLWriterBase, + hasProp = {}.hasOwnProperty; + + NodeType = require('./NodeType'); + + XMLWriterBase = require('./XMLWriterBase'); + + WriterState = require('./WriterState'); + + // Prints XML nodes to a stream + module.exports = XMLStreamWriter = class XMLStreamWriter extends XMLWriterBase { + // Initializes a new instance of `XMLStreamWriter` + + // `stream` output stream + // `options.pretty` pretty prints the result + // `options.indent` indentation string + // `options.newline` newline sequence + // `options.offset` a fixed number of indentations to add to every line + // `options.allowEmpty` do not self close empty element tags + // 'options.dontPrettyTextNodes' if any text is present in node, don't indent or LF + // `options.spaceBeforeSlash` add a space before the closing slash of empty elements + constructor(stream, options) { + super(options); + this.stream = stream; + } + + endline(node, options, level) { + if (node.isLastRootNode && options.state === WriterState.CloseTag) { + return ''; + } else { + return super.endline(node, options, level); + } + } + + document(doc, options) { + var child, i, j, k, len1, len2, ref, ref1, results; + ref = doc.children; + // set a flag so that we don't insert a newline after the last root level node + for (i = j = 0, len1 = ref.length; j < len1; i = ++j) { + child = ref[i]; + child.isLastRootNode = i === doc.children.length - 1; + } + options = this.filterOptions(options); + ref1 = doc.children; + results = []; + for (k = 0, len2 = ref1.length; k < len2; k++) { + child = ref1[k]; + results.push(this.writeChildNode(child, options, 0)); + } + return results; + } + + cdata(node, options, level) { + return this.stream.write(super.cdata(node, options, level)); + } + + comment(node, options, level) { + return this.stream.write(super.comment(node, options, level)); + } + + declaration(node, options, level) { + return this.stream.write(super.declaration(node, options, level)); + } + + docType(node, options, level) { + var child, j, len1, ref; + level || (level = 0); + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + this.stream.write(this.indent(node, options, level)); + this.stream.write(' 0) { + this.stream.write(' ['); + this.stream.write(this.endline(node, options, level)); + options.state = WriterState.InsideTag; + ref = node.children; + for (j = 0, len1 = ref.length; j < len1; j++) { + child = ref[j]; + this.writeChildNode(child, options, level + 1); + } + options.state = WriterState.CloseTag; + this.stream.write(']'); + } + // close tag + options.state = WriterState.CloseTag; + this.stream.write(options.spaceBeforeSlash + '>'); + this.stream.write(this.endline(node, options, level)); + options.state = WriterState.None; + return this.closeNode(node, options, level); + } + + element(node, options, level) { + var att, attLen, child, childNodeCount, firstChildNode, j, len, len1, name, prettySuppressed, r, ratt, ref, ref1, ref2, rline; + level || (level = 0); + // open tag + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '<' + node.name; + // attributes + if (options.pretty && options.width > 0) { + len = r.length; + ref = node.attribs; + for (name in ref) { + if (!hasProp.call(ref, name)) continue; + att = ref[name]; + ratt = this.attribute(att, options, level); + attLen = ratt.length; + if (len + attLen > options.width) { + rline = this.indent(node, options, level + 1) + ratt; + r += this.endline(node, options, level) + rline; + len = rline.length; + } else { + rline = ' ' + ratt; + r += rline; + len += rline.length; + } + } + } else { + ref1 = node.attribs; + for (name in ref1) { + if (!hasProp.call(ref1, name)) continue; + att = ref1[name]; + r += this.attribute(att, options, level); + } + } + this.stream.write(r); + childNodeCount = node.children.length; + firstChildNode = childNodeCount === 0 ? null : node.children[0]; + if (childNodeCount === 0 || node.children.every(function(e) { + return (e.type === NodeType.Text || e.type === NodeType.Raw || e.type === NodeType.CData) && e.value === ''; + })) { + // empty element + if (options.allowEmpty) { + this.stream.write('>'); + options.state = WriterState.CloseTag; + this.stream.write(''); + } else { + options.state = WriterState.CloseTag; + this.stream.write(options.spaceBeforeSlash + '/>'); + } + } else if (options.pretty && childNodeCount === 1 && (firstChildNode.type === NodeType.Text || firstChildNode.type === NodeType.Raw || firstChildNode.type === NodeType.CData) && (firstChildNode.value != null)) { + // do not indent text-only nodes + this.stream.write('>'); + options.state = WriterState.InsideTag; + options.suppressPrettyCount++; + prettySuppressed = true; + this.writeChildNode(firstChildNode, options, level + 1); + options.suppressPrettyCount--; + prettySuppressed = false; + options.state = WriterState.CloseTag; + this.stream.write(''); + } else { + this.stream.write('>' + this.endline(node, options, level)); + options.state = WriterState.InsideTag; + ref2 = node.children; + // inner tags + for (j = 0, len1 = ref2.length; j < len1; j++) { + child = ref2[j]; + this.writeChildNode(child, options, level + 1); + } + // close tag + options.state = WriterState.CloseTag; + this.stream.write(this.indent(node, options, level) + ''); + } + this.stream.write(this.endline(node, options, level)); + options.state = WriterState.None; + return this.closeNode(node, options, level); + } + + processingInstruction(node, options, level) { + return this.stream.write(super.processingInstruction(node, options, level)); + } + + raw(node, options, level) { + return this.stream.write(super.raw(node, options, level)); + } + + text(node, options, level) { + return this.stream.write(super.text(node, options, level)); + } + + dtdAttList(node, options, level) { + return this.stream.write(super.dtdAttList(node, options, level)); + } + + dtdElement(node, options, level) { + return this.stream.write(super.dtdElement(node, options, level)); + } + + dtdEntity(node, options, level) { + return this.stream.write(super.dtdEntity(node, options, level)); + } + + dtdNotation(node, options, level) { + return this.stream.write(super.dtdNotation(node, options, level)); + } + + }; + +}).call(this); + +},{"./NodeType":5,"./WriterState":7,"./XMLWriterBase":35}],32:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var XMLStringWriter, XMLWriterBase; + + XMLWriterBase = require('./XMLWriterBase'); + + // Prints XML nodes as plain text + module.exports = XMLStringWriter = class XMLStringWriter extends XMLWriterBase { + // Initializes a new instance of `XMLStringWriter` + + // `options.pretty` pretty prints the result + // `options.indent` indentation string + // `options.newline` newline sequence + // `options.offset` a fixed number of indentations to add to every line + // `options.allowEmpty` do not self close empty element tags + // 'options.dontPrettyTextNodes' if any text is present in node, don't indent or LF + // `options.spaceBeforeSlash` add a space before the closing slash of empty elements + constructor(options) { + super(options); + } + + document(doc, options) { + var child, i, len, r, ref; + options = this.filterOptions(options); + r = ''; + ref = doc.children; + for (i = 0, len = ref.length; i < len; i++) { + child = ref[i]; + r += this.writeChildNode(child, options, 0); + } + // remove trailing newline + if (options.pretty && r.slice(-options.newline.length) === options.newline) { + r = r.slice(0, -options.newline.length); + } + return r; + } + + }; + +}).call(this); + +},{"./XMLWriterBase":35}],33:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + // Converts values to strings + var XMLStringifier, + hasProp = {}.hasOwnProperty; + + module.exports = XMLStringifier = (function() { + class XMLStringifier { + // Initializes a new instance of `XMLStringifier` + + // `options.version` The version number string of the XML spec to validate against, e.g. 1.0 + // `options.noDoubleEncoding` whether existing html entities are encoded: true or false + // `options.stringify` a set of functions to use for converting values to strings + // `options.noValidation` whether values will be validated and escaped or returned as is + // `options.invalidCharReplacement` a character to replace invalid characters and disable character validation + constructor(options) { + var key, ref, value; + // Checks whether the given string contains legal characters + // Fails with an exception on error + + // `str` the string to check + this.assertLegalChar = this.assertLegalChar.bind(this); + // Checks whether the given string contains legal characters for a name + // Fails with an exception on error + + // `str` the string to check + this.assertLegalName = this.assertLegalName.bind(this); + options || (options = {}); + this.options = options; + if (!this.options.version) { + this.options.version = '1.0'; + } + ref = options.stringify || {}; + for (key in ref) { + if (!hasProp.call(ref, key)) continue; + value = ref[key]; + this[key] = value; + } + } + + // Defaults + name(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalName('' + val || ''); + } + + text(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar(this.textEscape('' + val || '')); + } + + cdata(val) { + if (this.options.noValidation) { + return val; + } + val = '' + val || ''; + val = val.replace(']]>', ']]]]>'); + return this.assertLegalChar(val); + } + + comment(val) { + if (this.options.noValidation) { + return val; + } + val = '' + val || ''; + if (val.match(/--/)) { + throw new Error("Comment text cannot contain double-hypen: " + val); + } + return this.assertLegalChar(val); + } + + raw(val) { + if (this.options.noValidation) { + return val; + } + return '' + val || ''; + } + + attValue(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar(this.attEscape(val = '' + val || '')); + } + + insTarget(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar('' + val || ''); + } + + insValue(val) { + if (this.options.noValidation) { + return val; + } + val = '' + val || ''; + if (val.match(/\?>/)) { + throw new Error("Invalid processing instruction value: " + val); + } + return this.assertLegalChar(val); + } + + xmlVersion(val) { + if (this.options.noValidation) { + return val; + } + val = '' + val || ''; + if (!val.match(/1\.[0-9]+/)) { + throw new Error("Invalid version number: " + val); + } + return val; + } + + xmlEncoding(val) { + if (this.options.noValidation) { + return val; + } + val = '' + val || ''; + if (!val.match(/^[A-Za-z](?:[A-Za-z0-9._-])*$/)) { + throw new Error("Invalid encoding: " + val); + } + return this.assertLegalChar(val); + } + + xmlStandalone(val) { + if (this.options.noValidation) { + return val; + } + if (val) { + return "yes"; + } else { + return "no"; + } + } + + dtdPubID(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar('' + val || ''); + } + + dtdSysID(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar('' + val || ''); + } + + dtdElementValue(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar('' + val || ''); + } + + dtdAttType(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar('' + val || ''); + } + + dtdAttDefault(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar('' + val || ''); + } + + dtdEntityValue(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar('' + val || ''); + } + + dtdNData(val) { + if (this.options.noValidation) { + return val; + } + return this.assertLegalChar('' + val || ''); + } + + assertLegalChar(str) { + var regex, res; + if (this.options.noValidation) { + return str; + } + if (this.options.version === '1.0') { + // Valid characters from https://www.w3.org/TR/xml/#charsets + // any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. + // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // This ES5 compatible Regexp has been generated using the "regenerate" NPM module: + // let xml_10_InvalidChars = regenerate() + // .addRange(0x0000, 0x0008) + // .add(0x000B, 0x000C) + // .addRange(0x000E, 0x001F) + // .addRange(0xD800, 0xDFFF) + // .addRange(0xFFFE, 0xFFFF) + regex = /[\0-\x08\x0B\f\x0E-\x1F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g; + if (this.options.invalidCharReplacement !== void 0) { + str = str.replace(regex, this.options.invalidCharReplacement); + } else if (res = str.match(regex)) { + throw new Error(`Invalid character in string: ${str} at index ${res.index}`); + } + } else if (this.options.version === '1.1') { + // Valid characters from https://www.w3.org/TR/xml11/#charsets + // any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. + // [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // This ES5 compatible Regexp has been generated using the "regenerate" NPM module: + // let xml_11_InvalidChars = regenerate() + // .add(0x0000) + // .addRange(0xD800, 0xDFFF) + // .addRange(0xFFFE, 0xFFFF) + regex = /[\0\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g; + if (this.options.invalidCharReplacement !== void 0) { + str = str.replace(regex, this.options.invalidCharReplacement); + } else if (res = str.match(regex)) { + throw new Error(`Invalid character in string: ${str} at index ${res.index}`); + } + } + return str; + } + + assertLegalName(str) { + var regex; + if (this.options.noValidation) { + return str; + } + str = this.assertLegalChar(str); + regex = /^([:A-Z_a-z\xC0-\xD6\xD8-\xF6\xF8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]|[\uD800-\uDB7F][\uDC00-\uDFFF])([\x2D\.0-:A-Z_a-z\xB7\xC0-\xD6\xD8-\xF6\xF8-\u037D\u037F-\u1FFF\u200C\u200D\u203F\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]|[\uD800-\uDB7F][\uDC00-\uDFFF])*$/; + if (!str.match(regex)) { + throw new Error(`Invalid character in name: ${str}`); + } + return str; + } + + // Escapes special characters in text + + // See http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping + + // `str` the string to escape + textEscape(str) { + var ampregex; + if (this.options.noValidation) { + return str; + } + ampregex = this.options.noDoubleEncoding ? /(?!&(lt|gt|amp|apos|quot);)&/g : /&/g; + return str.replace(ampregex, '&').replace(//g, '>').replace(/\r/g, ' '); + } + + // Escapes special characters in attribute values + + // See http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping + + // `str` the string to escape + attEscape(str) { + var ampregex; + if (this.options.noValidation) { + return str; + } + ampregex = this.options.noDoubleEncoding ? /(?!&(lt|gt|amp|apos|quot);)&/g : /&/g; + return str.replace(ampregex, '&').replace(/ 0) { + return new Array(indentLevel).join(options.indent); + } + } + return ''; + } + + // Returns the newline string + + // `node` current node + // `options` writer options + // `level` current indentation level + endline(node, options, level) { + if (!options.pretty || options.suppressPrettyCount) { + return ''; + } else { + return options.newline; + } + } + + attribute(att, options, level) { + var r; + this.openAttribute(att, options, level); + if (options.pretty && options.width > 0) { + r = att.name + '="' + att.value + '"'; + } else { + r = ' ' + att.name + '="' + att.value + '"'; + } + this.closeAttribute(att, options, level); + return r; + } + + cdata(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + comment(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + declaration(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + ''; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + docType(node, options, level) { + var child, i, len1, r, ref; + level || (level = 0); + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level); + r += ' 0) { + r += ' ['; + r += this.endline(node, options, level); + options.state = WriterState.InsideTag; + ref = node.children; + for (i = 0, len1 = ref.length; i < len1; i++) { + child = ref[i]; + r += this.writeChildNode(child, options, level + 1); + } + options.state = WriterState.CloseTag; + r += ']'; + } + // close tag + options.state = WriterState.CloseTag; + r += options.spaceBeforeSlash + '>'; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + element(node, options, level) { + var att, attLen, child, childNodeCount, firstChildNode, i, j, len, len1, len2, name, prettySuppressed, r, ratt, ref, ref1, ref2, ref3, rline; + level || (level = 0); + prettySuppressed = false; + // open tag + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '<' + node.name; + // attributes + if (options.pretty && options.width > 0) { + len = r.length; + ref = node.attribs; + for (name in ref) { + if (!hasProp.call(ref, name)) continue; + att = ref[name]; + ratt = this.attribute(att, options, level); + attLen = ratt.length; + if (len + attLen > options.width) { + rline = this.indent(node, options, level + 1) + ratt; + r += this.endline(node, options, level) + rline; + len = rline.length; + } else { + rline = ' ' + ratt; + r += rline; + len += rline.length; + } + } + } else { + ref1 = node.attribs; + for (name in ref1) { + if (!hasProp.call(ref1, name)) continue; + att = ref1[name]; + r += this.attribute(att, options, level); + } + } + childNodeCount = node.children.length; + firstChildNode = childNodeCount === 0 ? null : node.children[0]; + if (childNodeCount === 0 || node.children.every(function(e) { + return (e.type === NodeType.Text || e.type === NodeType.Raw || e.type === NodeType.CData) && e.value === ''; + })) { + // empty element + if (options.allowEmpty) { + r += '>'; + options.state = WriterState.CloseTag; + r += '' + this.endline(node, options, level); + } else { + options.state = WriterState.CloseTag; + r += options.spaceBeforeSlash + '/>' + this.endline(node, options, level); + } + } else if (options.pretty && childNodeCount === 1 && (firstChildNode.type === NodeType.Text || firstChildNode.type === NodeType.Raw || firstChildNode.type === NodeType.CData) && (firstChildNode.value != null)) { + // do not indent text-only nodes + r += '>'; + options.state = WriterState.InsideTag; + options.suppressPrettyCount++; + prettySuppressed = true; + r += this.writeChildNode(firstChildNode, options, level + 1); + options.suppressPrettyCount--; + prettySuppressed = false; + options.state = WriterState.CloseTag; + r += '' + this.endline(node, options, level); + } else { + // if ANY are a text node, then suppress pretty now + if (options.dontPrettyTextNodes) { + ref2 = node.children; + for (i = 0, len1 = ref2.length; i < len1; i++) { + child = ref2[i]; + if ((child.type === NodeType.Text || child.type === NodeType.Raw || child.type === NodeType.CData) && (child.value != null)) { + options.suppressPrettyCount++; + prettySuppressed = true; + break; + } + } + } + // close the opening tag, after dealing with newline + r += '>' + this.endline(node, options, level); + options.state = WriterState.InsideTag; + ref3 = node.children; + // inner tags + for (j = 0, len2 = ref3.length; j < len2; j++) { + child = ref3[j]; + r += this.writeChildNode(child, options, level + 1); + } + // close tag + options.state = WriterState.CloseTag; + r += this.indent(node, options, level) + ''; + if (prettySuppressed) { + options.suppressPrettyCount--; + } + r += this.endline(node, options, level); + options.state = WriterState.None; + } + this.closeNode(node, options, level); + return r; + } + + writeChildNode(node, options, level) { + switch (node.type) { + case NodeType.CData: + return this.cdata(node, options, level); + case NodeType.Comment: + return this.comment(node, options, level); + case NodeType.Element: + return this.element(node, options, level); + case NodeType.Raw: + return this.raw(node, options, level); + case NodeType.Text: + return this.text(node, options, level); + case NodeType.ProcessingInstruction: + return this.processingInstruction(node, options, level); + case NodeType.Dummy: + return ''; + case NodeType.Declaration: + return this.declaration(node, options, level); + case NodeType.DocType: + return this.docType(node, options, level); + case NodeType.AttributeDeclaration: + return this.dtdAttList(node, options, level); + case NodeType.ElementDeclaration: + return this.dtdElement(node, options, level); + case NodeType.EntityDeclaration: + return this.dtdEntity(node, options, level); + case NodeType.NotationDeclaration: + return this.dtdNotation(node, options, level); + default: + throw new Error("Unknown XML node type: " + node.constructor.name); + } + } + + processingInstruction(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + ''; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + raw(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level); + options.state = WriterState.InsideTag; + r += node.value; + options.state = WriterState.CloseTag; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + text(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level); + options.state = WriterState.InsideTag; + r += node.value; + options.state = WriterState.CloseTag; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + dtdAttList(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + dtdElement(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + dtdEntity(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + dtdNotation(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + openNode(node, options, level) {} + + closeNode(node, options, level) {} + + openAttribute(att, options, level) {} + + closeAttribute(att, options, level) {} + + }; + +}).call(this); + +},{"./NodeType":5,"./Utility":6,"./WriterState":7,"./XMLCData":9,"./XMLComment":11,"./XMLDTDAttList":16,"./XMLDTDElement":17,"./XMLDTDEntity":18,"./XMLDTDNotation":19,"./XMLDeclaration":20,"./XMLDocType":21,"./XMLDummy":24,"./XMLElement":25,"./XMLProcessingInstruction":29,"./XMLRaw":30,"./XMLText":34}],36:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction; + + ({assign, isFunction} = require('./Utility')); + + XMLDOMImplementation = require('./XMLDOMImplementation'); + + XMLDocument = require('./XMLDocument'); + + XMLDocumentCB = require('./XMLDocumentCB'); + + XMLStringWriter = require('./XMLStringWriter'); + + XMLStreamWriter = require('./XMLStreamWriter'); + + NodeType = require('./NodeType'); + + WriterState = require('./WriterState'); + + // Creates a new document and returns the root node for + // chain-building the document tree + + // `name` name of the root element + + // `xmldec.version` A version number string, e.g. 1.0 + // `xmldec.encoding` Encoding declaration, e.g. UTF-8 + // `xmldec.standalone` standalone document declaration: true or false + + // `doctype.pubID` public identifier of the external subset + // `doctype.sysID` system identifier of the external subset + + // `options.headless` whether XML declaration and doctype will be included: + // true or false + // `options.keepNullNodes` whether nodes with null values will be kept + // or ignored: true or false + // `options.keepNullAttributes` whether attributes with null values will be + // kept or ignored: true or false + // `options.ignoreDecorators` whether decorator strings will be ignored when + // converting JS objects: true or false + // `options.separateArrayItems` whether array items are created as separate + // nodes when passed as an object value: true or false + // `options.noDoubleEncoding` whether existing html entities are encoded: + // true or false + // `options.stringify` a set of functions to use for converting values to + // strings + // `options.writer` the default XML writer to use for converting nodes to + // string. If the default writer is not set, the built-in XMLStringWriter + // will be used instead. + module.exports.create = function(name, xmldec, doctype, options) { + var doc, root; + if (name == null) { + throw new Error("Root element needs a name."); + } + options = assign({}, xmldec, doctype, options); + // create the document node + doc = new XMLDocument(options); + // add the root node + root = doc.element(name); + // prolog + if (!options.headless) { + doc.declaration(options); + if ((options.pubID != null) || (options.sysID != null)) { + doc.dtd(options); + } + } + return root; + }; + + // Creates a new document and returns the document node for + // chain-building the document tree + + // `options.keepNullNodes` whether nodes with null values will be kept + // or ignored: true or false + // `options.keepNullAttributes` whether attributes with null values will be + // kept or ignored: true or false + // `options.ignoreDecorators` whether decorator strings will be ignored when + // converting JS objects: true or false + // `options.separateArrayItems` whether array items are created as separate + // nodes when passed as an object value: true or false + // `options.noDoubleEncoding` whether existing html entities are encoded: + // true or false + // `options.stringify` a set of functions to use for converting values to + // strings + // `options.writer` the default XML writer to use for converting nodes to + // string. If the default writer is not set, the built-in XMLStringWriter + // will be used instead. + + // `onData` the function to be called when a new chunk of XML is output. The + // string containing the XML chunk is passed to `onData` as its single + // argument. + // `onEnd` the function to be called when the XML document is completed with + // `end`. `onEnd` does not receive any arguments. + module.exports.begin = function(options, onData, onEnd) { + if (isFunction(options)) { + [onData, onEnd] = [options, onData]; + options = {}; + } + if (onData) { + return new XMLDocumentCB(options, onData, onEnd); + } else { + return new XMLDocument(options); + } + }; + + module.exports.stringWriter = function(options) { + return new XMLStringWriter(options); + }; + + module.exports.streamWriter = function(stream, options) { + return new XMLStreamWriter(stream, options); + }; + + module.exports.implementation = new XMLDOMImplementation(); + + module.exports.nodeType = NodeType; + + module.exports.writerState = WriterState; + +}).call(this); + +},{"./NodeType":5,"./Utility":6,"./WriterState":7,"./XMLDOMImplementation":14,"./XMLDocument":22,"./XMLDocumentCB":23,"./XMLStreamWriter":31,"./XMLStringWriter":32}]},{},[1])(1) +}); diff --git a/node_modules/plist/dist/plist-parse.js b/node_modules/plist/dist/plist-parse.js new file mode 100644 index 0000000..b8c7eb1 --- /dev/null +++ b/node_modules/plist/dist/plist-parse.js @@ -0,0 +1,7456 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.plist = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i, + * and raw CDATA nodes. + * + * @param {Element} node + * @returns {Boolean} + * @api private + */ + +function shouldIgnoreNode (node) { + return node.nodeType === TEXT_NODE + || node.nodeType === COMMENT_NODE + || node.nodeType === CDATA_NODE; +} + +/** + * Check if the node is empty. Some plist file has such node: + * + * this node shoud be ignored. + * + * @see https://github.com/TooTallNate/plist.js/issues/66 + * @param {Element} node + * @returns {Boolean} + * @api private + */ +function isEmptyNode(node){ + if(!node.childNodes || node.childNodes.length === 0) { + return true; + } else { + return false; + } +} + +function invariant(test, message) { + if (!test) { + throw new Error(message); + } +} + +/** + * Parses a Plist XML string. Returns an Object. + * + * @param {String} xml - the XML String to decode + * @returns {Mixed} the decoded value from the Plist XML + * @api public + */ + +function parse (xml) { + var doc = new DOMParser().parseFromString(xml); + invariant( + doc.documentElement.nodeName === 'plist', + 'malformed document. First element should be ' + ); + var plist = parsePlistXML(doc.documentElement); + + // the root node gets interpreted as an Array, + // so pull out the inner data first + if (plist.length == 1) plist = plist[0]; + + return plist; +} + +/** + * Convert an XML based plist document into a JSON representation. + * + * @param {Object} xml_node - current XML node in the plist + * @returns {Mixed} built up JSON object + * @api private + */ + +function parsePlistXML (node) { + var i, new_obj, key, val, new_arr, res, counter, type; + + if (!node) + return null; + + if (node.nodeName === 'plist') { + new_arr = []; + if (isEmptyNode(node)) { + return new_arr; + } + for (i=0; i < node.childNodes.length; i++) { + if (!shouldIgnoreNode(node.childNodes[i])) { + new_arr.push( parsePlistXML(node.childNodes[i])); + } + } + return new_arr; + } else if (node.nodeName === 'dict') { + new_obj = {}; + key = null; + counter = 0; + if (isEmptyNode(node)) { + return new_obj; + } + for (i=0; i < node.childNodes.length; i++) { + if (shouldIgnoreNode(node.childNodes[i])) continue; + if (counter % 2 === 0) { + invariant( + node.childNodes[i].nodeName === 'key', + 'Missing key while parsing .' + ); + key = parsePlistXML(node.childNodes[i]); + } else { + invariant( + node.childNodes[i].nodeName !== 'key', + 'Unexpected key "' + + parsePlistXML(node.childNodes[i]) + + '" while parsing .' + ); + new_obj[key] = parsePlistXML(node.childNodes[i]); + } + counter += 1; + } + if (counter % 2 === 1) { + new_obj[key] = ''; + } + + return new_obj; + + } else if (node.nodeName === 'array') { + new_arr = []; + if (isEmptyNode(node)) { + return new_arr; + } + for (i=0; i < node.childNodes.length; i++) { + if (!shouldIgnoreNode(node.childNodes[i])) { + res = parsePlistXML(node.childNodes[i]); + if (null != res) new_arr.push(res); + } + } + return new_arr; + + } else if (node.nodeName === '#text') { + // TODO: what should we do with text types? (CDATA sections) + + } else if (node.nodeName === 'key') { + if (isEmptyNode(node)) { + return ''; + } + + invariant( + node.childNodes[0].nodeValue !== '__proto__', + '__proto__ keys can lead to prototype pollution. More details on CVE-2022-22912' + ); + + return node.childNodes[0].nodeValue; + } else if (node.nodeName === 'string') { + res = ''; + if (isEmptyNode(node)) { + return res; + } + for (i=0; i < node.childNodes.length; i++) { + var type = node.childNodes[i].nodeType; + if (type === TEXT_NODE || type === CDATA_NODE) { + res += node.childNodes[i].nodeValue; + } + } + return res; + + } else if (node.nodeName === 'integer') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as integer.' + ); + return parseInt(node.childNodes[0].nodeValue, 10); + + } else if (node.nodeName === 'real') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as real.' + ); + res = ''; + for (i=0; i < node.childNodes.length; i++) { + if (node.childNodes[i].nodeType === TEXT_NODE) { + res += node.childNodes[i].nodeValue; + } + } + return parseFloat(res); + + } else if (node.nodeName === 'data') { + res = ''; + if (isEmptyNode(node)) { + return Buffer.from(res, 'base64'); + } + for (i=0; i < node.childNodes.length; i++) { + if (node.childNodes[i].nodeType === TEXT_NODE) { + res += node.childNodes[i].nodeValue.replace(/\s+/g, ''); + } + } + return Buffer.from(res, 'base64'); + + } else if (node.nodeName === 'date') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as Date.' + ) + return new Date(node.childNodes[0].nodeValue); + + } else if (node.nodeName === 'null') { + return null; + + } else if (node.nodeName === 'true') { + return true; + + } else if (node.nodeName === 'false') { + return false; + } else { + throw new Error('Invalid PLIST tag ' + node.nodeName); + } +} + +}).call(this)}).call(this,require("buffer").Buffer) +},{"@xmldom/xmldom":6,"buffer":9}],2:[function(require,module,exports){ +'use strict' + +/** + * Ponyfill for `Array.prototype.find` which is only available in ES6 runtimes. + * + * Works with anything that has a `length` property and index access properties, including NodeList. + * + * @template {unknown} T + * @param {Array | ({length:number, [number]: T})} list + * @param {function (item: T, index: number, list:Array | ({length:number, [number]: T})):boolean} predicate + * @param {Partial>?} ac `Array.prototype` by default, + * allows injecting a custom implementation in tests + * @returns {T | undefined} + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find + * @see https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.find + */ +function find(list, predicate, ac) { + if (ac === undefined) { + ac = Array.prototype; + } + if (list && typeof ac.find === 'function') { + return ac.find.call(list, predicate); + } + for (var i = 0; i < list.length; i++) { + if (Object.prototype.hasOwnProperty.call(list, i)) { + var item = list[i]; + if (predicate.call(undefined, item, i, list)) { + return item; + } + } + } +} + +/** + * "Shallow freezes" an object to render it immutable. + * Uses `Object.freeze` if available, + * otherwise the immutability is only in the type. + * + * Is used to create "enum like" objects. + * + * @template T + * @param {T} object the object to freeze + * @param {Pick = Object} oc `Object` by default, + * allows to inject custom object constructor for tests + * @returns {Readonly} + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze + */ +function freeze(object, oc) { + if (oc === undefined) { + oc = Object + } + return oc && typeof oc.freeze === 'function' ? oc.freeze(object) : object +} + +/** + * Since we can not rely on `Object.assign` we provide a simplified version + * that is sufficient for our needs. + * + * @param {Object} target + * @param {Object | null | undefined} source + * + * @returns {Object} target + * @throws TypeError if target is not an object + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + * @see https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.assign + */ +function assign(target, source) { + if (target === null || typeof target !== 'object') { + throw new TypeError('target is not an object') + } + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + return target +} + +/** + * All mime types that are allowed as input to `DOMParser.parseFromString` + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString#Argument02 MDN + * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#domparsersupportedtype WHATWG HTML Spec + * @see DOMParser.prototype.parseFromString + */ +var MIME_TYPE = freeze({ + /** + * `text/html`, the only mime type that triggers treating an XML document as HTML. + * + * @see DOMParser.SupportedType.isHTML + * @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration + * @see https://en.wikipedia.org/wiki/HTML Wikipedia + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString MDN + * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring WHATWG HTML Spec + */ + HTML: 'text/html', + + /** + * Helper method to check a mime type if it indicates an HTML document + * + * @param {string} [value] + * @returns {boolean} + * + * @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration + * @see https://en.wikipedia.org/wiki/HTML Wikipedia + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString MDN + * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring */ + isHTML: function (value) { + return value === MIME_TYPE.HTML + }, + + /** + * `application/xml`, the standard mime type for XML documents. + * + * @see https://www.iana.org/assignments/media-types/application/xml IANA MimeType registration + * @see https://tools.ietf.org/html/rfc7303#section-9.1 RFC 7303 + * @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia + */ + XML_APPLICATION: 'application/xml', + + /** + * `text/html`, an alias for `application/xml`. + * + * @see https://tools.ietf.org/html/rfc7303#section-9.2 RFC 7303 + * @see https://www.iana.org/assignments/media-types/text/xml IANA MimeType registration + * @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia + */ + XML_TEXT: 'text/xml', + + /** + * `application/xhtml+xml`, indicates an XML document that has the default HTML namespace, + * but is parsed as an XML document. + * + * @see https://www.iana.org/assignments/media-types/application/xhtml+xml IANA MimeType registration + * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument WHATWG DOM Spec + * @see https://en.wikipedia.org/wiki/XHTML Wikipedia + */ + XML_XHTML_APPLICATION: 'application/xhtml+xml', + + /** + * `image/svg+xml`, + * + * @see https://www.iana.org/assignments/media-types/image/svg+xml IANA MimeType registration + * @see https://www.w3.org/TR/SVG11/ W3C SVG 1.1 + * @see https://en.wikipedia.org/wiki/Scalable_Vector_Graphics Wikipedia + */ + XML_SVG_IMAGE: 'image/svg+xml', +}) + +/** + * Namespaces that are used in this code base. + * + * @see http://www.w3.org/TR/REC-xml-names + */ +var NAMESPACE = freeze({ + /** + * The XHTML namespace. + * + * @see http://www.w3.org/1999/xhtml + */ + HTML: 'http://www.w3.org/1999/xhtml', + + /** + * Checks if `uri` equals `NAMESPACE.HTML`. + * + * @param {string} [uri] + * + * @see NAMESPACE.HTML + */ + isHTML: function (uri) { + return uri === NAMESPACE.HTML + }, + + /** + * The SVG namespace. + * + * @see http://www.w3.org/2000/svg + */ + SVG: 'http://www.w3.org/2000/svg', + + /** + * The `xml:` namespace. + * + * @see http://www.w3.org/XML/1998/namespace + */ + XML: 'http://www.w3.org/XML/1998/namespace', + + /** + * The `xmlns:` namespace + * + * @see https://www.w3.org/2000/xmlns/ + */ + XMLNS: 'http://www.w3.org/2000/xmlns/', +}) + +exports.assign = assign; +exports.find = find; +exports.freeze = freeze; +exports.MIME_TYPE = MIME_TYPE; +exports.NAMESPACE = NAMESPACE; + +},{}],3:[function(require,module,exports){ +var conventions = require("./conventions"); +var dom = require('./dom') +var entities = require('./entities'); +var sax = require('./sax'); + +var DOMImplementation = dom.DOMImplementation; + +var NAMESPACE = conventions.NAMESPACE; + +var ParseError = sax.ParseError; +var XMLReader = sax.XMLReader; + +/** + * Normalizes line ending according to https://www.w3.org/TR/xml11/#sec-line-ends: + * + * > XML parsed entities are often stored in computer files which, + * > for editing convenience, are organized into lines. + * > These lines are typically separated by some combination + * > of the characters CARRIAGE RETURN (#xD) and LINE FEED (#xA). + * > + * > To simplify the tasks of applications, the XML processor must behave + * > as if it normalized all line breaks in external parsed entities (including the document entity) + * > on input, before parsing, by translating all of the following to a single #xA character: + * > + * > 1. the two-character sequence #xD #xA + * > 2. the two-character sequence #xD #x85 + * > 3. the single character #x85 + * > 4. the single character #x2028 + * > 5. any #xD character that is not immediately followed by #xA or #x85. + * + * @param {string} input + * @returns {string} + */ +function normalizeLineEndings(input) { + return input + .replace(/\r[\n\u0085]/g, '\n') + .replace(/[\r\u0085\u2028]/g, '\n') +} + +/** + * @typedef Locator + * @property {number} [columnNumber] + * @property {number} [lineNumber] + */ + +/** + * @typedef DOMParserOptions + * @property {DOMHandler} [domBuilder] + * @property {Function} [errorHandler] + * @property {(string) => string} [normalizeLineEndings] used to replace line endings before parsing + * defaults to `normalizeLineEndings` + * @property {Locator} [locator] + * @property {Record} [xmlns] + * + * @see normalizeLineEndings + */ + +/** + * The DOMParser interface provides the ability to parse XML or HTML source code + * from a string into a DOM `Document`. + * + * _xmldom is different from the spec in that it allows an `options` parameter, + * to override the default behavior._ + * + * @param {DOMParserOptions} [options] + * @constructor + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser + * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-parsing-and-serialization + */ +function DOMParser(options){ + this.options = options ||{locator:{}}; +} + +DOMParser.prototype.parseFromString = function(source,mimeType){ + var options = this.options; + var sax = new XMLReader(); + var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler + var errorHandler = options.errorHandler; + var locator = options.locator; + var defaultNSMap = options.xmlns||{}; + var isHTML = /\/x?html?$/.test(mimeType);//mimeType.toLowerCase().indexOf('html') > -1; + var entityMap = isHTML ? entities.HTML_ENTITIES : entities.XML_ENTITIES; + if(locator){ + domBuilder.setDocumentLocator(locator) + } + + sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator); + sax.domBuilder = options.domBuilder || domBuilder; + if(isHTML){ + defaultNSMap[''] = NAMESPACE.HTML; + } + defaultNSMap.xml = defaultNSMap.xml || NAMESPACE.XML; + var normalize = options.normalizeLineEndings || normalizeLineEndings; + if (source && typeof source === 'string') { + sax.parse( + normalize(source), + defaultNSMap, + entityMap + ) + } else { + sax.errorHandler.error('invalid doc source') + } + return domBuilder.doc; +} +function buildErrorHandler(errorImpl,domBuilder,locator){ + if(!errorImpl){ + if(domBuilder instanceof DOMHandler){ + return domBuilder; + } + errorImpl = domBuilder ; + } + var errorHandler = {} + var isCallback = errorImpl instanceof Function; + locator = locator||{} + function build(key){ + var fn = errorImpl[key]; + if(!fn && isCallback){ + fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl; + } + errorHandler[key] = fn && function(msg){ + fn('[xmldom '+key+']\t'+msg+_locator(locator)); + }||function(){}; + } + build('warning'); + build('error'); + build('fatalError'); + return errorHandler; +} + +//console.log('#\n\n\n\n\n\n\n####') +/** + * +ContentHandler+ErrorHandler + * +LexicalHandler+EntityResolver2 + * -DeclHandler-DTDHandler + * + * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler + * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2 + * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html + */ +function DOMHandler() { + this.cdata = false; +} +function position(locator,node){ + node.lineNumber = locator.lineNumber; + node.columnNumber = locator.columnNumber; +} +/** + * @see org.xml.sax.ContentHandler#startDocument + * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html + */ +DOMHandler.prototype = { + startDocument : function() { + this.doc = new DOMImplementation().createDocument(null, null, null); + if (this.locator) { + this.doc.documentURI = this.locator.systemId; + } + }, + startElement:function(namespaceURI, localName, qName, attrs) { + var doc = this.doc; + var el = doc.createElementNS(namespaceURI, qName||localName); + var len = attrs.length; + appendElement(this, el); + this.currentElement = el; + + this.locator && position(this.locator,el) + for (var i = 0 ; i < len; i++) { + var namespaceURI = attrs.getURI(i); + var value = attrs.getValue(i); + var qName = attrs.getQName(i); + var attr = doc.createAttributeNS(namespaceURI, qName); + this.locator &&position(attrs.getLocator(i),attr); + attr.value = attr.nodeValue = value; + el.setAttributeNode(attr) + } + }, + endElement:function(namespaceURI, localName, qName) { + var current = this.currentElement + var tagName = current.tagName; + this.currentElement = current.parentNode; + }, + startPrefixMapping:function(prefix, uri) { + }, + endPrefixMapping:function(prefix) { + }, + processingInstruction:function(target, data) { + var ins = this.doc.createProcessingInstruction(target, data); + this.locator && position(this.locator,ins) + appendElement(this, ins); + }, + ignorableWhitespace:function(ch, start, length) { + }, + characters:function(chars, start, length) { + chars = _toString.apply(this,arguments) + //console.log(chars) + if(chars){ + if (this.cdata) { + var charNode = this.doc.createCDATASection(chars); + } else { + var charNode = this.doc.createTextNode(chars); + } + if(this.currentElement){ + this.currentElement.appendChild(charNode); + }else if(/^\s*$/.test(chars)){ + this.doc.appendChild(charNode); + //process xml + } + this.locator && position(this.locator,charNode) + } + }, + skippedEntity:function(name) { + }, + endDocument:function() { + this.doc.normalize(); + }, + setDocumentLocator:function (locator) { + if(this.locator = locator){// && !('lineNumber' in locator)){ + locator.lineNumber = 0; + } + }, + //LexicalHandler + comment:function(chars, start, length) { + chars = _toString.apply(this,arguments) + var comm = this.doc.createComment(chars); + this.locator && position(this.locator,comm) + appendElement(this, comm); + }, + + startCDATA:function() { + //used in characters() methods + this.cdata = true; + }, + endCDATA:function() { + this.cdata = false; + }, + + startDTD:function(name, publicId, systemId) { + var impl = this.doc.implementation; + if (impl && impl.createDocumentType) { + var dt = impl.createDocumentType(name, publicId, systemId); + this.locator && position(this.locator,dt) + appendElement(this, dt); + this.doc.doctype = dt; + } + }, + /** + * @see org.xml.sax.ErrorHandler + * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html + */ + warning:function(error) { + console.warn('[xmldom warning]\t'+error,_locator(this.locator)); + }, + error:function(error) { + console.error('[xmldom error]\t'+error,_locator(this.locator)); + }, + fatalError:function(error) { + throw new ParseError(error, this.locator); + } +} +function _locator(l){ + if(l){ + return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']' + } +} +function _toString(chars,start,length){ + if(typeof chars == 'string'){ + return chars.substr(start,length) + }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") + if(chars.length >= start+length || start){ + return new java.lang.String(chars,start,length)+''; + } + return chars; + } +} + +/* + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html + * used method of org.xml.sax.ext.LexicalHandler: + * #comment(chars, start, length) + * #startCDATA() + * #endCDATA() + * #startDTD(name, publicId, systemId) + * + * + * IGNORED method of org.xml.sax.ext.LexicalHandler: + * #endDTD() + * #startEntity(name) + * #endEntity(name) + * + * + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html + * IGNORED method of org.xml.sax.ext.DeclHandler + * #attributeDecl(eName, aName, type, mode, value) + * #elementDecl(name, model) + * #externalEntityDecl(name, publicId, systemId) + * #internalEntityDecl(name, value) + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html + * IGNORED method of org.xml.sax.EntityResolver2 + * #resolveEntity(String name,String publicId,String baseURI,String systemId) + * #resolveEntity(publicId, systemId) + * #getExternalSubset(name, baseURI) + * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html + * IGNORED method of org.xml.sax.DTDHandler + * #notationDecl(name, publicId, systemId) {}; + * #unparsedEntityDecl(name, publicId, systemId, notationName) {}; + */ +"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){ + DOMHandler.prototype[key] = function(){return null} +}) + +/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */ +function appendElement (hander,node) { + if (!hander.currentElement) { + hander.doc.appendChild(node); + } else { + hander.currentElement.appendChild(node); + } +}//appendChild and setAttributeNS are preformance key + +exports.__DOMHandler = DOMHandler; +exports.normalizeLineEndings = normalizeLineEndings; +exports.DOMParser = DOMParser; + +},{"./conventions":2,"./dom":4,"./entities":5,"./sax":7}],4:[function(require,module,exports){ +var conventions = require("./conventions"); + +var find = conventions.find; +var NAMESPACE = conventions.NAMESPACE; + +/** + * A prerequisite for `[].filter`, to drop elements that are empty + * @param {string} input + * @returns {boolean} + */ +function notEmptyString (input) { + return input !== '' +} +/** + * @see https://infra.spec.whatwg.org/#split-on-ascii-whitespace + * @see https://infra.spec.whatwg.org/#ascii-whitespace + * + * @param {string} input + * @returns {string[]} (can be empty) + */ +function splitOnASCIIWhitespace(input) { + // U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, U+0020 SPACE + return input ? input.split(/[\t\n\f\r ]+/).filter(notEmptyString) : [] +} + +/** + * Adds element as a key to current if it is not already present. + * + * @param {Record} current + * @param {string} element + * @returns {Record} + */ +function orderedSetReducer (current, element) { + if (!current.hasOwnProperty(element)) { + current[element] = true; + } + return current; +} + +/** + * @see https://infra.spec.whatwg.org/#ordered-set + * @param {string} input + * @returns {string[]} + */ +function toOrderedSet(input) { + if (!input) return []; + var list = splitOnASCIIWhitespace(input); + return Object.keys(list.reduce(orderedSetReducer, {})) +} + +/** + * Uses `list.indexOf` to implement something like `Array.prototype.includes`, + * which we can not rely on being available. + * + * @param {any[]} list + * @returns {function(any): boolean} + */ +function arrayIncludes (list) { + return function(element) { + return list && list.indexOf(element) !== -1; + } +} + +function copy(src,dest){ + for(var p in src){ + if (Object.prototype.hasOwnProperty.call(src, p)) { + dest[p] = src[p]; + } + } +} + +/** +^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));? +^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));? + */ +function _extends(Class,Super){ + var pt = Class.prototype; + if(!(pt instanceof Super)){ + function t(){}; + t.prototype = Super.prototype; + t = new t(); + copy(pt,t); + Class.prototype = pt = t; + } + if(pt.constructor != Class){ + if(typeof Class != 'function'){ + console.error("unknown Class:"+Class) + } + pt.constructor = Class + } +} + +// Node Types +var NodeType = {} +var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1; +var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2; +var TEXT_NODE = NodeType.TEXT_NODE = 3; +var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4; +var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5; +var ENTITY_NODE = NodeType.ENTITY_NODE = 6; +var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7; +var COMMENT_NODE = NodeType.COMMENT_NODE = 8; +var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9; +var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10; +var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11; +var NOTATION_NODE = NodeType.NOTATION_NODE = 12; + +// ExceptionCode +var ExceptionCode = {} +var ExceptionMessage = {}; +var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = ((ExceptionMessage[1]="Index size error"),1); +var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = ((ExceptionMessage[2]="DOMString size error"),2); +var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = ((ExceptionMessage[3]="Hierarchy request error"),3); +var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = ((ExceptionMessage[4]="Wrong document"),4); +var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = ((ExceptionMessage[5]="Invalid character"),5); +var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = ((ExceptionMessage[6]="No data allowed"),6); +var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7); +var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = ((ExceptionMessage[8]="Not found"),8); +var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = ((ExceptionMessage[9]="Not supported"),9); +var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = ((ExceptionMessage[10]="Attribute in use"),10); +//level2 +var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = ((ExceptionMessage[11]="Invalid state"),11); +var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = ((ExceptionMessage[12]="Syntax error"),12); +var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = ((ExceptionMessage[13]="Invalid modification"),13); +var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = ((ExceptionMessage[14]="Invalid namespace"),14); +var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = ((ExceptionMessage[15]="Invalid access"),15); + +/** + * DOM Level 2 + * Object DOMException + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html + * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html + */ +function DOMException(code, message) { + if(message instanceof Error){ + var error = message; + }else{ + error = this; + Error.call(this, ExceptionMessage[code]); + this.message = ExceptionMessage[code]; + if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException); + } + error.code = code; + if(message) this.message = this.message + ": " + message; + return error; +}; +DOMException.prototype = Error.prototype; +copy(ExceptionCode,DOMException) + +/** + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177 + * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live. + * The items in the NodeList are accessible via an integral index, starting from 0. + */ +function NodeList() { +}; +NodeList.prototype = { + /** + * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive. + * @standard level1 + */ + length:0, + /** + * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null. + * @standard level1 + * @param index unsigned long + * Index into the collection. + * @return Node + * The node at the indexth position in the NodeList, or null if that is not a valid index. + */ + item: function(index) { + return this[index] || null; + }, + toString:function(isHTML,nodeFilter){ + for(var buf = [], i = 0;i=0){ + var lastIndex = list.length-1 + while(i0 || key == 'xmlns'){ +// return null; +// } + //console.log() + var i = this.length; + while(i--){ + var attr = this[i]; + //console.log(attr.nodeName,key) + if(attr.nodeName == key){ + return attr; + } + } + }, + setNamedItem: function(attr) { + var el = attr.ownerElement; + if(el && el!=this._ownerElement){ + throw new DOMException(INUSE_ATTRIBUTE_ERR); + } + var oldAttr = this.getNamedItem(attr.nodeName); + _addNamedNode(this._ownerElement,this,attr,oldAttr); + return oldAttr; + }, + /* returns Node */ + setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR + var el = attr.ownerElement, oldAttr; + if(el && el!=this._ownerElement){ + throw new DOMException(INUSE_ATTRIBUTE_ERR); + } + oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName); + _addNamedNode(this._ownerElement,this,attr,oldAttr); + return oldAttr; + }, + + /* returns Node */ + removeNamedItem: function(key) { + var attr = this.getNamedItem(key); + _removeNamedNode(this._ownerElement,this,attr); + return attr; + + + },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR + + //for level2 + removeNamedItemNS:function(namespaceURI,localName){ + var attr = this.getNamedItemNS(namespaceURI,localName); + _removeNamedNode(this._ownerElement,this,attr); + return attr; + }, + getNamedItemNS: function(namespaceURI, localName) { + var i = this.length; + while(i--){ + var node = this[i]; + if(node.localName == localName && node.namespaceURI == namespaceURI){ + return node; + } + } + return null; + } +}; + +/** + * The DOMImplementation interface represents an object providing methods + * which are not dependent on any particular document. + * Such an object is returned by the `Document.implementation` property. + * + * __The individual methods describe the differences compared to the specs.__ + * + * @constructor + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation MDN + * @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 DOM Level 1 Core (Initial) + * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490 DOM Level 2 Core + * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-102161490 DOM Level 3 Core + * @see https://dom.spec.whatwg.org/#domimplementation DOM Living Standard + */ +function DOMImplementation() { +} + +DOMImplementation.prototype = { + /** + * The DOMImplementation.hasFeature() method returns a Boolean flag indicating if a given feature is supported. + * The different implementations fairly diverged in what kind of features were reported. + * The latest version of the spec settled to force this method to always return true, where the functionality was accurate and in use. + * + * @deprecated It is deprecated and modern browsers return true in all cases. + * + * @param {string} feature + * @param {string} [version] + * @returns {boolean} always true + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/hasFeature MDN + * @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-5CED94D7 DOM Level 1 Core + * @see https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature DOM Living Standard + */ + hasFeature: function(feature, version) { + return true; + }, + /** + * Creates an XML Document object of the specified type with its document element. + * + * __It behaves slightly different from the description in the living standard__: + * - There is no interface/class `XMLDocument`, it returns a `Document` instance. + * - `contentType`, `encoding`, `mode`, `origin`, `url` fields are currently not declared. + * - this implementation is not validating names or qualified names + * (when parsing XML strings, the SAX parser takes care of that) + * + * @param {string|null} namespaceURI + * @param {string} qualifiedName + * @param {DocumentType=null} doctype + * @returns {Document} + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocument MDN + * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocument DOM Level 2 Core (initial) + * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument DOM Level 2 Core + * + * @see https://dom.spec.whatwg.org/#validate-and-extract DOM: Validate and extract + * @see https://www.w3.org/TR/xml/#NT-NameStartChar XML Spec: Names + * @see https://www.w3.org/TR/xml-names/#ns-qualnames XML Namespaces: Qualified names + */ + createDocument: function(namespaceURI, qualifiedName, doctype){ + var doc = new Document(); + doc.implementation = this; + doc.childNodes = new NodeList(); + doc.doctype = doctype || null; + if (doctype){ + doc.appendChild(doctype); + } + if (qualifiedName){ + var root = doc.createElementNS(namespaceURI, qualifiedName); + doc.appendChild(root); + } + return doc; + }, + /** + * Returns a doctype, with the given `qualifiedName`, `publicId`, and `systemId`. + * + * __This behavior is slightly different from the in the specs__: + * - this implementation is not validating names or qualified names + * (when parsing XML strings, the SAX parser takes care of that) + * + * @param {string} qualifiedName + * @param {string} [publicId] + * @param {string} [systemId] + * @returns {DocumentType} which can either be used with `DOMImplementation.createDocument` upon document creation + * or can be put into the document via methods like `Node.insertBefore()` or `Node.replaceChild()` + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocumentType MDN + * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocType DOM Level 2 Core + * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype DOM Living Standard + * + * @see https://dom.spec.whatwg.org/#validate-and-extract DOM: Validate and extract + * @see https://www.w3.org/TR/xml/#NT-NameStartChar XML Spec: Names + * @see https://www.w3.org/TR/xml-names/#ns-qualnames XML Namespaces: Qualified names + */ + createDocumentType: function(qualifiedName, publicId, systemId){ + var node = new DocumentType(); + node.name = qualifiedName; + node.nodeName = qualifiedName; + node.publicId = publicId || ''; + node.systemId = systemId || ''; + + return node; + } +}; + + +/** + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247 + */ + +function Node() { +}; + +Node.prototype = { + firstChild : null, + lastChild : null, + previousSibling : null, + nextSibling : null, + attributes : null, + parentNode : null, + childNodes : null, + ownerDocument : null, + nodeValue : null, + namespaceURI : null, + prefix : null, + localName : null, + // Modified in DOM Level 2: + insertBefore:function(newChild, refChild){//raises + return _insertBefore(this,newChild,refChild); + }, + replaceChild:function(newChild, oldChild){//raises + _insertBefore(this, newChild,oldChild, assertPreReplacementValidityInDocument); + if(oldChild){ + this.removeChild(oldChild); + } + }, + removeChild:function(oldChild){ + return _removeChild(this,oldChild); + }, + appendChild:function(newChild){ + return this.insertBefore(newChild,null); + }, + hasChildNodes:function(){ + return this.firstChild != null; + }, + cloneNode:function(deep){ + return cloneNode(this.ownerDocument||this,this,deep); + }, + // Modified in DOM Level 2: + normalize:function(){ + var child = this.firstChild; + while(child){ + var next = child.nextSibling; + if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){ + this.removeChild(next); + child.appendData(next.data); + }else{ + child.normalize(); + child = next; + } + } + }, + // Introduced in DOM Level 2: + isSupported:function(feature, version){ + return this.ownerDocument.implementation.hasFeature(feature,version); + }, + // Introduced in DOM Level 2: + hasAttributes:function(){ + return this.attributes.length>0; + }, + /** + * Look up the prefix associated to the given namespace URI, starting from this node. + * **The default namespace declarations are ignored by this method.** + * See Namespace Prefix Lookup for details on the algorithm used by this method. + * + * _Note: The implementation seems to be incomplete when compared to the algorithm described in the specs._ + * + * @param {string | null} namespaceURI + * @returns {string | null} + * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespacePrefix + * @see https://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#lookupNamespacePrefixAlgo + * @see https://dom.spec.whatwg.org/#dom-node-lookupprefix + * @see https://github.com/xmldom/xmldom/issues/322 + */ + lookupPrefix:function(namespaceURI){ + var el = this; + while(el){ + var map = el._nsMap; + //console.dir(map) + if(map){ + for(var n in map){ + if (Object.prototype.hasOwnProperty.call(map, n) && map[n] === namespaceURI) { + return n; + } + } + } + el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; + } + return null; + }, + // Introduced in DOM Level 3: + lookupNamespaceURI:function(prefix){ + var el = this; + while(el){ + var map = el._nsMap; + //console.dir(map) + if(map){ + if(Object.prototype.hasOwnProperty.call(map, prefix)){ + return map[prefix] ; + } + } + el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; + } + return null; + }, + // Introduced in DOM Level 3: + isDefaultNamespace:function(namespaceURI){ + var prefix = this.lookupPrefix(namespaceURI); + return prefix == null; + } +}; + + +function _xmlEncoder(c){ + return c == '<' && '<' || + c == '>' && '>' || + c == '&' && '&' || + c == '"' && '"' || + '&#'+c.charCodeAt()+';' +} + + +copy(NodeType,Node); +copy(NodeType,Node.prototype); + +/** + * @param callback return true for continue,false for break + * @return boolean true: break visit; + */ +function _visitNode(node,callback){ + if(callback(node)){ + return true; + } + if(node = node.firstChild){ + do{ + if(_visitNode(node,callback)){return true} + }while(node=node.nextSibling) + } +} + + + +function Document(){ + this.ownerDocument = this; +} + +function _onAddAttribute(doc,el,newAttr){ + doc && doc._inc++; + var ns = newAttr.namespaceURI ; + if(ns === NAMESPACE.XMLNS){ + //update namespace + el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value + } +} + +function _onRemoveAttribute(doc,el,newAttr,remove){ + doc && doc._inc++; + var ns = newAttr.namespaceURI ; + if(ns === NAMESPACE.XMLNS){ + //update namespace + delete el._nsMap[newAttr.prefix?newAttr.localName:''] + } +} + +/** + * Updates `el.childNodes`, updating the indexed items and it's `length`. + * Passing `newChild` means it will be appended. + * Otherwise it's assumed that an item has been removed, + * and `el.firstNode` and it's `.nextSibling` are used + * to walk the current list of child nodes. + * + * @param {Document} doc + * @param {Node} el + * @param {Node} [newChild] + * @private + */ +function _onUpdateChild (doc, el, newChild) { + if(doc && doc._inc){ + doc._inc++; + //update childNodes + var cs = el.childNodes; + if (newChild) { + cs[cs.length++] = newChild; + } else { + var child = el.firstChild; + var i = 0; + while (child) { + cs[i++] = child; + child = child.nextSibling; + } + cs.length = i; + delete cs[cs.length]; + } + } +} + +/** + * Removes the connections between `parentNode` and `child` + * and any existing `child.previousSibling` or `child.nextSibling`. + * + * @see https://github.com/xmldom/xmldom/issues/135 + * @see https://github.com/xmldom/xmldom/issues/145 + * + * @param {Node} parentNode + * @param {Node} child + * @returns {Node} the child that was removed. + * @private + */ +function _removeChild (parentNode, child) { + var previous = child.previousSibling; + var next = child.nextSibling; + if (previous) { + previous.nextSibling = next; + } else { + parentNode.firstChild = next; + } + if (next) { + next.previousSibling = previous; + } else { + parentNode.lastChild = previous; + } + child.parentNode = null; + child.previousSibling = null; + child.nextSibling = null; + _onUpdateChild(parentNode.ownerDocument, parentNode); + return child; +} + +/** + * Returns `true` if `node` can be a parent for insertion. + * @param {Node} node + * @returns {boolean} + */ +function hasValidParentNodeType(node) { + return ( + node && + (node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE || node.nodeType === Node.ELEMENT_NODE) + ); +} + +/** + * Returns `true` if `node` can be inserted according to it's `nodeType`. + * @param {Node} node + * @returns {boolean} + */ +function hasInsertableNodeType(node) { + return ( + node && + (isElementNode(node) || + isTextNode(node) || + isDocTypeNode(node) || + node.nodeType === Node.DOCUMENT_FRAGMENT_NODE || + node.nodeType === Node.COMMENT_NODE || + node.nodeType === Node.PROCESSING_INSTRUCTION_NODE) + ); +} + +/** + * Returns true if `node` is a DOCTYPE node + * @param {Node} node + * @returns {boolean} + */ +function isDocTypeNode(node) { + return node && node.nodeType === Node.DOCUMENT_TYPE_NODE; +} + +/** + * Returns true if the node is an element + * @param {Node} node + * @returns {boolean} + */ +function isElementNode(node) { + return node && node.nodeType === Node.ELEMENT_NODE; +} +/** + * Returns true if `node` is a text node + * @param {Node} node + * @returns {boolean} + */ +function isTextNode(node) { + return node && node.nodeType === Node.TEXT_NODE; +} + +/** + * Check if en element node can be inserted before `child`, or at the end if child is falsy, + * according to the presence and position of a doctype node on the same level. + * + * @param {Document} doc The document node + * @param {Node} child the node that would become the nextSibling if the element would be inserted + * @returns {boolean} `true` if an element can be inserted before child + * @private + * https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity + */ +function isElementInsertionPossible(doc, child) { + var parentChildNodes = doc.childNodes || []; + if (find(parentChildNodes, isElementNode) || isDocTypeNode(child)) { + return false; + } + var docTypeNode = find(parentChildNodes, isDocTypeNode); + return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child)); +} + +/** + * Check if en element node can be inserted before `child`, or at the end if child is falsy, + * according to the presence and position of a doctype node on the same level. + * + * @param {Node} doc The document node + * @param {Node} child the node that would become the nextSibling if the element would be inserted + * @returns {boolean} `true` if an element can be inserted before child + * @private + * https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity + */ +function isElementReplacementPossible(doc, child) { + var parentChildNodes = doc.childNodes || []; + + function hasElementChildThatIsNotChild(node) { + return isElementNode(node) && node !== child; + } + + if (find(parentChildNodes, hasElementChildThatIsNotChild)) { + return false; + } + var docTypeNode = find(parentChildNodes, isDocTypeNode); + return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child)); +} + +/** + * @private + * Steps 1-5 of the checks before inserting and before replacing a child are the same. + * + * @param {Node} parent the parent node to insert `node` into + * @param {Node} node the node to insert + * @param {Node=} child the node that should become the `nextSibling` of `node` + * @returns {Node} + * @throws DOMException for several node combinations that would create a DOM that is not well-formed. + * @throws DOMException if `child` is provided but is not a child of `parent`. + * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity + * @see https://dom.spec.whatwg.org/#concept-node-replace + */ +function assertPreInsertionValidity1to5(parent, node, child) { + // 1. If `parent` is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException. + if (!hasValidParentNodeType(parent)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Unexpected parent node type ' + parent.nodeType); + } + // 2. If `node` is a host-including inclusive ancestor of `parent`, then throw a "HierarchyRequestError" DOMException. + // not implemented! + // 3. If `child` is non-null and its parent is not `parent`, then throw a "NotFoundError" DOMException. + if (child && child.parentNode !== parent) { + throw new DOMException(NOT_FOUND_ERR, 'child not in parent'); + } + if ( + // 4. If `node` is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException. + !hasInsertableNodeType(node) || + // 5. If either `node` is a Text node and `parent` is a document, + // the sax parser currently adds top level text nodes, this will be fixed in 0.9.0 + // || (node.nodeType === Node.TEXT_NODE && parent.nodeType === Node.DOCUMENT_NODE) + // or `node` is a doctype and `parent` is not a document, then throw a "HierarchyRequestError" DOMException. + (isDocTypeNode(node) && parent.nodeType !== Node.DOCUMENT_NODE) + ) { + throw new DOMException( + HIERARCHY_REQUEST_ERR, + 'Unexpected node type ' + node.nodeType + ' for parent node type ' + parent.nodeType + ); + } +} + +/** + * @private + * Step 6 of the checks before inserting and before replacing a child are different. + * + * @param {Document} parent the parent node to insert `node` into + * @param {Node} node the node to insert + * @param {Node | undefined} child the node that should become the `nextSibling` of `node` + * @returns {Node} + * @throws DOMException for several node combinations that would create a DOM that is not well-formed. + * @throws DOMException if `child` is provided but is not a child of `parent`. + * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity + * @see https://dom.spec.whatwg.org/#concept-node-replace + */ +function assertPreInsertionValidityInDocument(parent, node, child) { + var parentChildNodes = parent.childNodes || []; + var nodeChildNodes = node.childNodes || []; + + // DocumentFragment + if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { + var nodeChildElements = nodeChildNodes.filter(isElementNode); + // If node has more than one element child or has a Text node child. + if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment'); + } + // Otherwise, if `node` has one element child and either `parent` has an element child, + // `child` is a doctype, or `child` is non-null and a doctype is following `child`. + if (nodeChildElements.length === 1 && !isElementInsertionPossible(parent, child)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype'); + } + } + // Element + if (isElementNode(node)) { + // `parent` has an element child, `child` is a doctype, + // or `child` is non-null and a doctype is following `child`. + if (!isElementInsertionPossible(parent, child)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype'); + } + } + // DocumentType + if (isDocTypeNode(node)) { + // `parent` has a doctype child, + if (find(parentChildNodes, isDocTypeNode)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed'); + } + var parentElementChild = find(parentChildNodes, isElementNode); + // `child` is non-null and an element is preceding `child`, + if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element'); + } + // or `child` is null and `parent` has an element child. + if (!child && parentElementChild) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Doctype can not be appended since element is present'); + } + } +} + +/** + * @private + * Step 6 of the checks before inserting and before replacing a child are different. + * + * @param {Document} parent the parent node to insert `node` into + * @param {Node} node the node to insert + * @param {Node | undefined} child the node that should become the `nextSibling` of `node` + * @returns {Node} + * @throws DOMException for several node combinations that would create a DOM that is not well-formed. + * @throws DOMException if `child` is provided but is not a child of `parent`. + * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity + * @see https://dom.spec.whatwg.org/#concept-node-replace + */ +function assertPreReplacementValidityInDocument(parent, node, child) { + var parentChildNodes = parent.childNodes || []; + var nodeChildNodes = node.childNodes || []; + + // DocumentFragment + if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { + var nodeChildElements = nodeChildNodes.filter(isElementNode); + // If `node` has more than one element child or has a Text node child. + if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment'); + } + // Otherwise, if `node` has one element child and either `parent` has an element child that is not `child` or a doctype is following `child`. + if (nodeChildElements.length === 1 && !isElementReplacementPossible(parent, child)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype'); + } + } + // Element + if (isElementNode(node)) { + // `parent` has an element child that is not `child` or a doctype is following `child`. + if (!isElementReplacementPossible(parent, child)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype'); + } + } + // DocumentType + if (isDocTypeNode(node)) { + function hasDoctypeChildThatIsNotChild(node) { + return isDocTypeNode(node) && node !== child; + } + + // `parent` has a doctype child that is not `child`, + if (find(parentChildNodes, hasDoctypeChildThatIsNotChild)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed'); + } + var parentElementChild = find(parentChildNodes, isElementNode); + // or an element is preceding `child`. + if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) { + throw new DOMException(HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element'); + } + } +} + +/** + * @private + * @param {Node} parent the parent node to insert `node` into + * @param {Node} node the node to insert + * @param {Node=} child the node that should become the `nextSibling` of `node` + * @returns {Node} + * @throws DOMException for several node combinations that would create a DOM that is not well-formed. + * @throws DOMException if `child` is provided but is not a child of `parent`. + * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity + */ +function _insertBefore(parent, node, child, _inDocumentAssertion) { + // To ensure pre-insertion validity of a node into a parent before a child, run these steps: + assertPreInsertionValidity1to5(parent, node, child); + + // If parent is a document, and any of the statements below, switched on the interface node implements, + // are true, then throw a "HierarchyRequestError" DOMException. + if (parent.nodeType === Node.DOCUMENT_NODE) { + (_inDocumentAssertion || assertPreInsertionValidityInDocument)(parent, node, child); + } + + var cp = node.parentNode; + if(cp){ + cp.removeChild(node);//remove and update + } + if(node.nodeType === DOCUMENT_FRAGMENT_NODE){ + var newFirst = node.firstChild; + if (newFirst == null) { + return node; + } + var newLast = node.lastChild; + }else{ + newFirst = newLast = node; + } + var pre = child ? child.previousSibling : parent.lastChild; + + newFirst.previousSibling = pre; + newLast.nextSibling = child; + + + if(pre){ + pre.nextSibling = newFirst; + }else{ + parent.firstChild = newFirst; + } + if(child == null){ + parent.lastChild = newLast; + }else{ + child.previousSibling = newLast; + } + do{ + newFirst.parentNode = parent; + }while(newFirst !== newLast && (newFirst= newFirst.nextSibling)) + _onUpdateChild(parent.ownerDocument||parent, parent); + //console.log(parent.lastChild.nextSibling == null) + if (node.nodeType == DOCUMENT_FRAGMENT_NODE) { + node.firstChild = node.lastChild = null; + } + return node; +} + +/** + * Appends `newChild` to `parentNode`. + * If `newChild` is already connected to a `parentNode` it is first removed from it. + * + * @see https://github.com/xmldom/xmldom/issues/135 + * @see https://github.com/xmldom/xmldom/issues/145 + * @param {Node} parentNode + * @param {Node} newChild + * @returns {Node} + * @private + */ +function _appendSingleChild (parentNode, newChild) { + if (newChild.parentNode) { + newChild.parentNode.removeChild(newChild); + } + newChild.parentNode = parentNode; + newChild.previousSibling = parentNode.lastChild; + newChild.nextSibling = null; + if (newChild.previousSibling) { + newChild.previousSibling.nextSibling = newChild; + } else { + parentNode.firstChild = newChild; + } + parentNode.lastChild = newChild; + _onUpdateChild(parentNode.ownerDocument, parentNode, newChild); + return newChild; +} + +Document.prototype = { + //implementation : null, + nodeName : '#document', + nodeType : DOCUMENT_NODE, + /** + * The DocumentType node of the document. + * + * @readonly + * @type DocumentType + */ + doctype : null, + documentElement : null, + _inc : 1, + + insertBefore : function(newChild, refChild){//raises + if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){ + var child = newChild.firstChild; + while(child){ + var next = child.nextSibling; + this.insertBefore(child,refChild); + child = next; + } + return newChild; + } + _insertBefore(this, newChild, refChild); + newChild.ownerDocument = this; + if (this.documentElement === null && newChild.nodeType === ELEMENT_NODE) { + this.documentElement = newChild; + } + + return newChild; + }, + removeChild : function(oldChild){ + if(this.documentElement == oldChild){ + this.documentElement = null; + } + return _removeChild(this,oldChild); + }, + replaceChild: function (newChild, oldChild) { + //raises + _insertBefore(this, newChild, oldChild, assertPreReplacementValidityInDocument); + newChild.ownerDocument = this; + if (oldChild) { + this.removeChild(oldChild); + } + if (isElementNode(newChild)) { + this.documentElement = newChild; + } + }, + // Introduced in DOM Level 2: + importNode : function(importedNode,deep){ + return importNode(this,importedNode,deep); + }, + // Introduced in DOM Level 2: + getElementById : function(id){ + var rtv = null; + _visitNode(this.documentElement,function(node){ + if(node.nodeType == ELEMENT_NODE){ + if(node.getAttribute('id') == id){ + rtv = node; + return true; + } + } + }) + return rtv; + }, + + /** + * The `getElementsByClassName` method of `Document` interface returns an array-like object + * of all child elements which have **all** of the given class name(s). + * + * Returns an empty list if `classeNames` is an empty string or only contains HTML white space characters. + * + * + * Warning: This is a live LiveNodeList. + * Changes in the DOM will reflect in the array as the changes occur. + * If an element selected by this array no longer qualifies for the selector, + * it will automatically be removed. Be aware of this for iteration purposes. + * + * @param {string} classNames is a string representing the class name(s) to match; multiple class names are separated by (ASCII-)whitespace + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName + * @see https://dom.spec.whatwg.org/#concept-getelementsbyclassname + */ + getElementsByClassName: function(classNames) { + var classNamesSet = toOrderedSet(classNames) + return new LiveNodeList(this, function(base) { + var ls = []; + if (classNamesSet.length > 0) { + _visitNode(base.documentElement, function(node) { + if(node !== base && node.nodeType === ELEMENT_NODE) { + var nodeClassNames = node.getAttribute('class') + // can be null if the attribute does not exist + if (nodeClassNames) { + // before splitting and iterating just compare them for the most common case + var matches = classNames === nodeClassNames; + if (!matches) { + var nodeClassNamesSet = toOrderedSet(nodeClassNames) + matches = classNamesSet.every(arrayIncludes(nodeClassNamesSet)) + } + if(matches) { + ls.push(node); + } + } + } + }); + } + return ls; + }); + }, + + //document factory method: + createElement : function(tagName){ + var node = new Element(); + node.ownerDocument = this; + node.nodeName = tagName; + node.tagName = tagName; + node.localName = tagName; + node.childNodes = new NodeList(); + var attrs = node.attributes = new NamedNodeMap(); + attrs._ownerElement = node; + return node; + }, + createDocumentFragment : function(){ + var node = new DocumentFragment(); + node.ownerDocument = this; + node.childNodes = new NodeList(); + return node; + }, + createTextNode : function(data){ + var node = new Text(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createComment : function(data){ + var node = new Comment(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createCDATASection : function(data){ + var node = new CDATASection(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createProcessingInstruction : function(target,data){ + var node = new ProcessingInstruction(); + node.ownerDocument = this; + node.tagName = node.target = target; + node.nodeValue= node.data = data; + return node; + }, + createAttribute : function(name){ + var node = new Attr(); + node.ownerDocument = this; + node.name = name; + node.nodeName = name; + node.localName = name; + node.specified = true; + return node; + }, + createEntityReference : function(name){ + var node = new EntityReference(); + node.ownerDocument = this; + node.nodeName = name; + return node; + }, + // Introduced in DOM Level 2: + createElementNS : function(namespaceURI,qualifiedName){ + var node = new Element(); + var pl = qualifiedName.split(':'); + var attrs = node.attributes = new NamedNodeMap(); + node.childNodes = new NodeList(); + node.ownerDocument = this; + node.nodeName = qualifiedName; + node.tagName = qualifiedName; + node.namespaceURI = namespaceURI; + if(pl.length == 2){ + node.prefix = pl[0]; + node.localName = pl[1]; + }else{ + //el.prefix = null; + node.localName = qualifiedName; + } + attrs._ownerElement = node; + return node; + }, + // Introduced in DOM Level 2: + createAttributeNS : function(namespaceURI,qualifiedName){ + var node = new Attr(); + var pl = qualifiedName.split(':'); + node.ownerDocument = this; + node.nodeName = qualifiedName; + node.name = qualifiedName; + node.namespaceURI = namespaceURI; + node.specified = true; + if(pl.length == 2){ + node.prefix = pl[0]; + node.localName = pl[1]; + }else{ + //el.prefix = null; + node.localName = qualifiedName; + } + return node; + } +}; +_extends(Document,Node); + + +function Element() { + this._nsMap = {}; +}; +Element.prototype = { + nodeType : ELEMENT_NODE, + hasAttribute : function(name){ + return this.getAttributeNode(name)!=null; + }, + getAttribute : function(name){ + var attr = this.getAttributeNode(name); + return attr && attr.value || ''; + }, + getAttributeNode : function(name){ + return this.attributes.getNamedItem(name); + }, + setAttribute : function(name, value){ + var attr = this.ownerDocument.createAttribute(name); + attr.value = attr.nodeValue = "" + value; + this.setAttributeNode(attr) + }, + removeAttribute : function(name){ + var attr = this.getAttributeNode(name) + attr && this.removeAttributeNode(attr); + }, + + //four real opeartion method + appendChild:function(newChild){ + if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ + return this.insertBefore(newChild,null); + }else{ + return _appendSingleChild(this,newChild); + } + }, + setAttributeNode : function(newAttr){ + return this.attributes.setNamedItem(newAttr); + }, + setAttributeNodeNS : function(newAttr){ + return this.attributes.setNamedItemNS(newAttr); + }, + removeAttributeNode : function(oldAttr){ + //console.log(this == oldAttr.ownerElement) + return this.attributes.removeNamedItem(oldAttr.nodeName); + }, + //get real attribute name,and remove it by removeAttributeNode + removeAttributeNS : function(namespaceURI, localName){ + var old = this.getAttributeNodeNS(namespaceURI, localName); + old && this.removeAttributeNode(old); + }, + + hasAttributeNS : function(namespaceURI, localName){ + return this.getAttributeNodeNS(namespaceURI, localName)!=null; + }, + getAttributeNS : function(namespaceURI, localName){ + var attr = this.getAttributeNodeNS(namespaceURI, localName); + return attr && attr.value || ''; + }, + setAttributeNS : function(namespaceURI, qualifiedName, value){ + var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName); + attr.value = attr.nodeValue = "" + value; + this.setAttributeNode(attr) + }, + getAttributeNodeNS : function(namespaceURI, localName){ + return this.attributes.getNamedItemNS(namespaceURI, localName); + }, + + getElementsByTagName : function(tagName){ + return new LiveNodeList(this,function(base){ + var ls = []; + _visitNode(base,function(node){ + if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){ + ls.push(node); + } + }); + return ls; + }); + }, + getElementsByTagNameNS : function(namespaceURI, localName){ + return new LiveNodeList(this,function(base){ + var ls = []; + _visitNode(base,function(node){ + if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){ + ls.push(node); + } + }); + return ls; + + }); + } +}; +Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName; +Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS; + + +_extends(Element,Node); +function Attr() { +}; +Attr.prototype.nodeType = ATTRIBUTE_NODE; +_extends(Attr,Node); + + +function CharacterData() { +}; +CharacterData.prototype = { + data : '', + substringData : function(offset, count) { + return this.data.substring(offset, offset+count); + }, + appendData: function(text) { + text = this.data+text; + this.nodeValue = this.data = text; + this.length = text.length; + }, + insertData: function(offset,text) { + this.replaceData(offset,0,text); + + }, + appendChild:function(newChild){ + throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]) + }, + deleteData: function(offset, count) { + this.replaceData(offset,count,""); + }, + replaceData: function(offset, count, text) { + var start = this.data.substring(0,offset); + var end = this.data.substring(offset+count); + text = start + text + end; + this.nodeValue = this.data = text; + this.length = text.length; + } +} +_extends(CharacterData,Node); +function Text() { +}; +Text.prototype = { + nodeName : "#text", + nodeType : TEXT_NODE, + splitText : function(offset) { + var text = this.data; + var newText = text.substring(offset); + text = text.substring(0, offset); + this.data = this.nodeValue = text; + this.length = text.length; + var newNode = this.ownerDocument.createTextNode(newText); + if(this.parentNode){ + this.parentNode.insertBefore(newNode, this.nextSibling); + } + return newNode; + } +} +_extends(Text,CharacterData); +function Comment() { +}; +Comment.prototype = { + nodeName : "#comment", + nodeType : COMMENT_NODE +} +_extends(Comment,CharacterData); + +function CDATASection() { +}; +CDATASection.prototype = { + nodeName : "#cdata-section", + nodeType : CDATA_SECTION_NODE +} +_extends(CDATASection,CharacterData); + + +function DocumentType() { +}; +DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE; +_extends(DocumentType,Node); + +function Notation() { +}; +Notation.prototype.nodeType = NOTATION_NODE; +_extends(Notation,Node); + +function Entity() { +}; +Entity.prototype.nodeType = ENTITY_NODE; +_extends(Entity,Node); + +function EntityReference() { +}; +EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE; +_extends(EntityReference,Node); + +function DocumentFragment() { +}; +DocumentFragment.prototype.nodeName = "#document-fragment"; +DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE; +_extends(DocumentFragment,Node); + + +function ProcessingInstruction() { +} +ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE; +_extends(ProcessingInstruction,Node); +function XMLSerializer(){} +XMLSerializer.prototype.serializeToString = function(node,isHtml,nodeFilter){ + return nodeSerializeToString.call(node,isHtml,nodeFilter); +} +Node.prototype.toString = nodeSerializeToString; +function nodeSerializeToString(isHtml,nodeFilter){ + var buf = []; + var refNode = this.nodeType == 9 && this.documentElement || this; + var prefix = refNode.prefix; + var uri = refNode.namespaceURI; + + if(uri && prefix == null){ + //console.log(prefix) + var prefix = refNode.lookupPrefix(uri); + if(prefix == null){ + //isHTML = true; + var visibleNamespaces=[ + {namespace:uri,prefix:null} + //{namespace:uri,prefix:''} + ] + } + } + serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces); + //console.log('###',this.nodeType,uri,prefix,buf.join('')) + return buf.join(''); +} + +function needNamespaceDefine(node, isHTML, visibleNamespaces) { + var prefix = node.prefix || ''; + var uri = node.namespaceURI; + // According to [Namespaces in XML 1.0](https://www.w3.org/TR/REC-xml-names/#ns-using) , + // and more specifically https://www.w3.org/TR/REC-xml-names/#nsc-NoPrefixUndecl : + // > In a namespace declaration for a prefix [...], the attribute value MUST NOT be empty. + // in a similar manner [Namespaces in XML 1.1](https://www.w3.org/TR/xml-names11/#ns-using) + // and more specifically https://www.w3.org/TR/xml-names11/#nsc-NSDeclared : + // > [...] Furthermore, the attribute value [...] must not be an empty string. + // so serializing empty namespace value like xmlns:ds="" would produce an invalid XML document. + if (!uri) { + return false; + } + if (prefix === "xml" && uri === NAMESPACE.XML || uri === NAMESPACE.XMLNS) { + return false; + } + + var i = visibleNamespaces.length + while (i--) { + var ns = visibleNamespaces[i]; + // get namespace prefix + if (ns.prefix === prefix) { + return ns.namespace !== uri; + } + } + return true; +} +/** + * Well-formed constraint: No < in Attribute Values + * > The replacement text of any entity referred to directly or indirectly + * > in an attribute value must not contain a <. + * @see https://www.w3.org/TR/xml11/#CleanAttrVals + * @see https://www.w3.org/TR/xml11/#NT-AttValue + * + * Literal whitespace other than space that appear in attribute values + * are serialized as their entity references, so they will be preserved. + * (In contrast to whitespace literals in the input which are normalized to spaces) + * @see https://www.w3.org/TR/xml11/#AVNormalize + * @see https://w3c.github.io/DOM-Parsing/#serializing-an-element-s-attributes + */ +function addSerializedAttribute(buf, qualifiedName, value) { + buf.push(' ', qualifiedName, '="', value.replace(/[<>&"\t\n\r]/g, _xmlEncoder), '"') +} + +function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){ + if (!visibleNamespaces) { + visibleNamespaces = []; + } + + if(nodeFilter){ + node = nodeFilter(node); + if(node){ + if(typeof node == 'string'){ + buf.push(node); + return; + } + }else{ + return; + } + //buf.sort.apply(attrs, attributeSorter); + } + + switch(node.nodeType){ + case ELEMENT_NODE: + var attrs = node.attributes; + var len = attrs.length; + var child = node.firstChild; + var nodeName = node.tagName; + + isHTML = NAMESPACE.isHTML(node.namespaceURI) || isHTML + + var prefixedNodeName = nodeName + if (!isHTML && !node.prefix && node.namespaceURI) { + var defaultNS + // lookup current default ns from `xmlns` attribute + for (var ai = 0; ai < attrs.length; ai++) { + if (attrs.item(ai).name === 'xmlns') { + defaultNS = attrs.item(ai).value + break + } + } + if (!defaultNS) { + // lookup current default ns in visibleNamespaces + for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) { + var namespace = visibleNamespaces[nsi] + if (namespace.prefix === '' && namespace.namespace === node.namespaceURI) { + defaultNS = namespace.namespace + break + } + } + } + if (defaultNS !== node.namespaceURI) { + for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) { + var namespace = visibleNamespaces[nsi] + if (namespace.namespace === node.namespaceURI) { + if (namespace.prefix) { + prefixedNodeName = namespace.prefix + ':' + nodeName + } + break + } + } + } + } + + buf.push('<', prefixedNodeName); + + for(var i=0;i'); + //if is cdata child node + if(isHTML && /^script$/i.test(nodeName)){ + while(child){ + if(child.data){ + buf.push(child.data); + }else{ + serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces.slice()); + } + child = child.nextSibling; + } + }else + { + while(child){ + serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces.slice()); + child = child.nextSibling; + } + } + buf.push(''); + }else{ + buf.push('/>'); + } + // remove added visible namespaces + //visibleNamespaces.length = startVisibleNamespaces; + return; + case DOCUMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: + var child = node.firstChild; + while(child){ + serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces.slice()); + child = child.nextSibling; + } + return; + case ATTRIBUTE_NODE: + return addSerializedAttribute(buf, node.name, node.value); + case TEXT_NODE: + /** + * The ampersand character (&) and the left angle bracket (<) must not appear in their literal form, + * except when used as markup delimiters, or within a comment, a processing instruction, or a CDATA section. + * If they are needed elsewhere, they must be escaped using either numeric character references or the strings + * `&` and `<` respectively. + * The right angle bracket (>) may be represented using the string " > ", and must, for compatibility, + * be escaped using either `>` or a character reference when it appears in the string `]]>` in content, + * when that string is not marking the end of a CDATA section. + * + * In the content of elements, character data is any string of characters + * which does not contain the start-delimiter of any markup + * and does not include the CDATA-section-close delimiter, `]]>`. + * + * @see https://www.w3.org/TR/xml/#NT-CharData + * @see https://w3c.github.io/DOM-Parsing/#xml-serializing-a-text-node + */ + return buf.push(node.data + .replace(/[<&>]/g,_xmlEncoder) + ); + case CDATA_SECTION_NODE: + return buf.push( ''); + case COMMENT_NODE: + return buf.push( ""); + case DOCUMENT_TYPE_NODE: + var pubid = node.publicId; + var sysid = node.systemId; + buf.push(''); + }else if(sysid && sysid!='.'){ + buf.push(' SYSTEM ', sysid, '>'); + }else{ + var sub = node.internalSubset; + if(sub){ + buf.push(" [",sub,"]"); + } + buf.push(">"); + } + return; + case PROCESSING_INSTRUCTION_NODE: + return buf.push( ""); + case ENTITY_REFERENCE_NODE: + return buf.push( '&',node.nodeName,';'); + //case ENTITY_NODE: + //case NOTATION_NODE: + default: + buf.push('??',node.nodeName); + } +} +function importNode(doc,node,deep){ + var node2; + switch (node.nodeType) { + case ELEMENT_NODE: + node2 = node.cloneNode(false); + node2.ownerDocument = doc; + //var attrs = node2.attributes; + //var len = attrs.length; + //for(var i=0;i', + lt: '<', + quot: '"', +}); + +/** + * A map of all entities that are detected in an HTML document. + * They contain all entries from `XML_ENTITIES`. + * + * @see XML_ENTITIES + * @see DOMParser.parseFromString + * @see DOMImplementation.prototype.createHTMLDocument + * @see https://html.spec.whatwg.org/#named-character-references WHATWG HTML(5) Spec + * @see https://html.spec.whatwg.org/entities.json JSON + * @see https://www.w3.org/TR/xml-entity-names/ W3C XML Entity Names + * @see https://www.w3.org/TR/html4/sgml/entities.html W3C HTML4/SGML + * @see https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Character_entity_references_in_HTML Wikipedia (HTML) + * @see https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Entities_representing_special_characters_in_XHTML Wikpedia (XHTML) + */ +exports.HTML_ENTITIES = freeze({ + Aacute: '\u00C1', + aacute: '\u00E1', + Abreve: '\u0102', + abreve: '\u0103', + ac: '\u223E', + acd: '\u223F', + acE: '\u223E\u0333', + Acirc: '\u00C2', + acirc: '\u00E2', + acute: '\u00B4', + Acy: '\u0410', + acy: '\u0430', + AElig: '\u00C6', + aelig: '\u00E6', + af: '\u2061', + Afr: '\uD835\uDD04', + afr: '\uD835\uDD1E', + Agrave: '\u00C0', + agrave: '\u00E0', + alefsym: '\u2135', + aleph: '\u2135', + Alpha: '\u0391', + alpha: '\u03B1', + Amacr: '\u0100', + amacr: '\u0101', + amalg: '\u2A3F', + AMP: '\u0026', + amp: '\u0026', + And: '\u2A53', + and: '\u2227', + andand: '\u2A55', + andd: '\u2A5C', + andslope: '\u2A58', + andv: '\u2A5A', + ang: '\u2220', + ange: '\u29A4', + angle: '\u2220', + angmsd: '\u2221', + angmsdaa: '\u29A8', + angmsdab: '\u29A9', + angmsdac: '\u29AA', + angmsdad: '\u29AB', + angmsdae: '\u29AC', + angmsdaf: '\u29AD', + angmsdag: '\u29AE', + angmsdah: '\u29AF', + angrt: '\u221F', + angrtvb: '\u22BE', + angrtvbd: '\u299D', + angsph: '\u2222', + angst: '\u00C5', + angzarr: '\u237C', + Aogon: '\u0104', + aogon: '\u0105', + Aopf: '\uD835\uDD38', + aopf: '\uD835\uDD52', + ap: '\u2248', + apacir: '\u2A6F', + apE: '\u2A70', + ape: '\u224A', + apid: '\u224B', + apos: '\u0027', + ApplyFunction: '\u2061', + approx: '\u2248', + approxeq: '\u224A', + Aring: '\u00C5', + aring: '\u00E5', + Ascr: '\uD835\uDC9C', + ascr: '\uD835\uDCB6', + Assign: '\u2254', + ast: '\u002A', + asymp: '\u2248', + asympeq: '\u224D', + Atilde: '\u00C3', + atilde: '\u00E3', + Auml: '\u00C4', + auml: '\u00E4', + awconint: '\u2233', + awint: '\u2A11', + backcong: '\u224C', + backepsilon: '\u03F6', + backprime: '\u2035', + backsim: '\u223D', + backsimeq: '\u22CD', + Backslash: '\u2216', + Barv: '\u2AE7', + barvee: '\u22BD', + Barwed: '\u2306', + barwed: '\u2305', + barwedge: '\u2305', + bbrk: '\u23B5', + bbrktbrk: '\u23B6', + bcong: '\u224C', + Bcy: '\u0411', + bcy: '\u0431', + bdquo: '\u201E', + becaus: '\u2235', + Because: '\u2235', + because: '\u2235', + bemptyv: '\u29B0', + bepsi: '\u03F6', + bernou: '\u212C', + Bernoullis: '\u212C', + Beta: '\u0392', + beta: '\u03B2', + beth: '\u2136', + between: '\u226C', + Bfr: '\uD835\uDD05', + bfr: '\uD835\uDD1F', + bigcap: '\u22C2', + bigcirc: '\u25EF', + bigcup: '\u22C3', + bigodot: '\u2A00', + bigoplus: '\u2A01', + bigotimes: '\u2A02', + bigsqcup: '\u2A06', + bigstar: '\u2605', + bigtriangledown: '\u25BD', + bigtriangleup: '\u25B3', + biguplus: '\u2A04', + bigvee: '\u22C1', + bigwedge: '\u22C0', + bkarow: '\u290D', + blacklozenge: '\u29EB', + blacksquare: '\u25AA', + blacktriangle: '\u25B4', + blacktriangledown: '\u25BE', + blacktriangleleft: '\u25C2', + blacktriangleright: '\u25B8', + blank: '\u2423', + blk12: '\u2592', + blk14: '\u2591', + blk34: '\u2593', + block: '\u2588', + bne: '\u003D\u20E5', + bnequiv: '\u2261\u20E5', + bNot: '\u2AED', + bnot: '\u2310', + Bopf: '\uD835\uDD39', + bopf: '\uD835\uDD53', + bot: '\u22A5', + bottom: '\u22A5', + bowtie: '\u22C8', + boxbox: '\u29C9', + boxDL: '\u2557', + boxDl: '\u2556', + boxdL: '\u2555', + boxdl: '\u2510', + boxDR: '\u2554', + boxDr: '\u2553', + boxdR: '\u2552', + boxdr: '\u250C', + boxH: '\u2550', + boxh: '\u2500', + boxHD: '\u2566', + boxHd: '\u2564', + boxhD: '\u2565', + boxhd: '\u252C', + boxHU: '\u2569', + boxHu: '\u2567', + boxhU: '\u2568', + boxhu: '\u2534', + boxminus: '\u229F', + boxplus: '\u229E', + boxtimes: '\u22A0', + boxUL: '\u255D', + boxUl: '\u255C', + boxuL: '\u255B', + boxul: '\u2518', + boxUR: '\u255A', + boxUr: '\u2559', + boxuR: '\u2558', + boxur: '\u2514', + boxV: '\u2551', + boxv: '\u2502', + boxVH: '\u256C', + boxVh: '\u256B', + boxvH: '\u256A', + boxvh: '\u253C', + boxVL: '\u2563', + boxVl: '\u2562', + boxvL: '\u2561', + boxvl: '\u2524', + boxVR: '\u2560', + boxVr: '\u255F', + boxvR: '\u255E', + boxvr: '\u251C', + bprime: '\u2035', + Breve: '\u02D8', + breve: '\u02D8', + brvbar: '\u00A6', + Bscr: '\u212C', + bscr: '\uD835\uDCB7', + bsemi: '\u204F', + bsim: '\u223D', + bsime: '\u22CD', + bsol: '\u005C', + bsolb: '\u29C5', + bsolhsub: '\u27C8', + bull: '\u2022', + bullet: '\u2022', + bump: '\u224E', + bumpE: '\u2AAE', + bumpe: '\u224F', + Bumpeq: '\u224E', + bumpeq: '\u224F', + Cacute: '\u0106', + cacute: '\u0107', + Cap: '\u22D2', + cap: '\u2229', + capand: '\u2A44', + capbrcup: '\u2A49', + capcap: '\u2A4B', + capcup: '\u2A47', + capdot: '\u2A40', + CapitalDifferentialD: '\u2145', + caps: '\u2229\uFE00', + caret: '\u2041', + caron: '\u02C7', + Cayleys: '\u212D', + ccaps: '\u2A4D', + Ccaron: '\u010C', + ccaron: '\u010D', + Ccedil: '\u00C7', + ccedil: '\u00E7', + Ccirc: '\u0108', + ccirc: '\u0109', + Cconint: '\u2230', + ccups: '\u2A4C', + ccupssm: '\u2A50', + Cdot: '\u010A', + cdot: '\u010B', + cedil: '\u00B8', + Cedilla: '\u00B8', + cemptyv: '\u29B2', + cent: '\u00A2', + CenterDot: '\u00B7', + centerdot: '\u00B7', + Cfr: '\u212D', + cfr: '\uD835\uDD20', + CHcy: '\u0427', + chcy: '\u0447', + check: '\u2713', + checkmark: '\u2713', + Chi: '\u03A7', + chi: '\u03C7', + cir: '\u25CB', + circ: '\u02C6', + circeq: '\u2257', + circlearrowleft: '\u21BA', + circlearrowright: '\u21BB', + circledast: '\u229B', + circledcirc: '\u229A', + circleddash: '\u229D', + CircleDot: '\u2299', + circledR: '\u00AE', + circledS: '\u24C8', + CircleMinus: '\u2296', + CirclePlus: '\u2295', + CircleTimes: '\u2297', + cirE: '\u29C3', + cire: '\u2257', + cirfnint: '\u2A10', + cirmid: '\u2AEF', + cirscir: '\u29C2', + ClockwiseContourIntegral: '\u2232', + CloseCurlyDoubleQuote: '\u201D', + CloseCurlyQuote: '\u2019', + clubs: '\u2663', + clubsuit: '\u2663', + Colon: '\u2237', + colon: '\u003A', + Colone: '\u2A74', + colone: '\u2254', + coloneq: '\u2254', + comma: '\u002C', + commat: '\u0040', + comp: '\u2201', + compfn: '\u2218', + complement: '\u2201', + complexes: '\u2102', + cong: '\u2245', + congdot: '\u2A6D', + Congruent: '\u2261', + Conint: '\u222F', + conint: '\u222E', + ContourIntegral: '\u222E', + Copf: '\u2102', + copf: '\uD835\uDD54', + coprod: '\u2210', + Coproduct: '\u2210', + COPY: '\u00A9', + copy: '\u00A9', + copysr: '\u2117', + CounterClockwiseContourIntegral: '\u2233', + crarr: '\u21B5', + Cross: '\u2A2F', + cross: '\u2717', + Cscr: '\uD835\uDC9E', + cscr: '\uD835\uDCB8', + csub: '\u2ACF', + csube: '\u2AD1', + csup: '\u2AD0', + csupe: '\u2AD2', + ctdot: '\u22EF', + cudarrl: '\u2938', + cudarrr: '\u2935', + cuepr: '\u22DE', + cuesc: '\u22DF', + cularr: '\u21B6', + cularrp: '\u293D', + Cup: '\u22D3', + cup: '\u222A', + cupbrcap: '\u2A48', + CupCap: '\u224D', + cupcap: '\u2A46', + cupcup: '\u2A4A', + cupdot: '\u228D', + cupor: '\u2A45', + cups: '\u222A\uFE00', + curarr: '\u21B7', + curarrm: '\u293C', + curlyeqprec: '\u22DE', + curlyeqsucc: '\u22DF', + curlyvee: '\u22CE', + curlywedge: '\u22CF', + curren: '\u00A4', + curvearrowleft: '\u21B6', + curvearrowright: '\u21B7', + cuvee: '\u22CE', + cuwed: '\u22CF', + cwconint: '\u2232', + cwint: '\u2231', + cylcty: '\u232D', + Dagger: '\u2021', + dagger: '\u2020', + daleth: '\u2138', + Darr: '\u21A1', + dArr: '\u21D3', + darr: '\u2193', + dash: '\u2010', + Dashv: '\u2AE4', + dashv: '\u22A3', + dbkarow: '\u290F', + dblac: '\u02DD', + Dcaron: '\u010E', + dcaron: '\u010F', + Dcy: '\u0414', + dcy: '\u0434', + DD: '\u2145', + dd: '\u2146', + ddagger: '\u2021', + ddarr: '\u21CA', + DDotrahd: '\u2911', + ddotseq: '\u2A77', + deg: '\u00B0', + Del: '\u2207', + Delta: '\u0394', + delta: '\u03B4', + demptyv: '\u29B1', + dfisht: '\u297F', + Dfr: '\uD835\uDD07', + dfr: '\uD835\uDD21', + dHar: '\u2965', + dharl: '\u21C3', + dharr: '\u21C2', + DiacriticalAcute: '\u00B4', + DiacriticalDot: '\u02D9', + DiacriticalDoubleAcute: '\u02DD', + DiacriticalGrave: '\u0060', + DiacriticalTilde: '\u02DC', + diam: '\u22C4', + Diamond: '\u22C4', + diamond: '\u22C4', + diamondsuit: '\u2666', + diams: '\u2666', + die: '\u00A8', + DifferentialD: '\u2146', + digamma: '\u03DD', + disin: '\u22F2', + div: '\u00F7', + divide: '\u00F7', + divideontimes: '\u22C7', + divonx: '\u22C7', + DJcy: '\u0402', + djcy: '\u0452', + dlcorn: '\u231E', + dlcrop: '\u230D', + dollar: '\u0024', + Dopf: '\uD835\uDD3B', + dopf: '\uD835\uDD55', + Dot: '\u00A8', + dot: '\u02D9', + DotDot: '\u20DC', + doteq: '\u2250', + doteqdot: '\u2251', + DotEqual: '\u2250', + dotminus: '\u2238', + dotplus: '\u2214', + dotsquare: '\u22A1', + doublebarwedge: '\u2306', + DoubleContourIntegral: '\u222F', + DoubleDot: '\u00A8', + DoubleDownArrow: '\u21D3', + DoubleLeftArrow: '\u21D0', + DoubleLeftRightArrow: '\u21D4', + DoubleLeftTee: '\u2AE4', + DoubleLongLeftArrow: '\u27F8', + DoubleLongLeftRightArrow: '\u27FA', + DoubleLongRightArrow: '\u27F9', + DoubleRightArrow: '\u21D2', + DoubleRightTee: '\u22A8', + DoubleUpArrow: '\u21D1', + DoubleUpDownArrow: '\u21D5', + DoubleVerticalBar: '\u2225', + DownArrow: '\u2193', + Downarrow: '\u21D3', + downarrow: '\u2193', + DownArrowBar: '\u2913', + DownArrowUpArrow: '\u21F5', + DownBreve: '\u0311', + downdownarrows: '\u21CA', + downharpoonleft: '\u21C3', + downharpoonright: '\u21C2', + DownLeftRightVector: '\u2950', + DownLeftTeeVector: '\u295E', + DownLeftVector: '\u21BD', + DownLeftVectorBar: '\u2956', + DownRightTeeVector: '\u295F', + DownRightVector: '\u21C1', + DownRightVectorBar: '\u2957', + DownTee: '\u22A4', + DownTeeArrow: '\u21A7', + drbkarow: '\u2910', + drcorn: '\u231F', + drcrop: '\u230C', + Dscr: '\uD835\uDC9F', + dscr: '\uD835\uDCB9', + DScy: '\u0405', + dscy: '\u0455', + dsol: '\u29F6', + Dstrok: '\u0110', + dstrok: '\u0111', + dtdot: '\u22F1', + dtri: '\u25BF', + dtrif: '\u25BE', + duarr: '\u21F5', + duhar: '\u296F', + dwangle: '\u29A6', + DZcy: '\u040F', + dzcy: '\u045F', + dzigrarr: '\u27FF', + Eacute: '\u00C9', + eacute: '\u00E9', + easter: '\u2A6E', + Ecaron: '\u011A', + ecaron: '\u011B', + ecir: '\u2256', + Ecirc: '\u00CA', + ecirc: '\u00EA', + ecolon: '\u2255', + Ecy: '\u042D', + ecy: '\u044D', + eDDot: '\u2A77', + Edot: '\u0116', + eDot: '\u2251', + edot: '\u0117', + ee: '\u2147', + efDot: '\u2252', + Efr: '\uD835\uDD08', + efr: '\uD835\uDD22', + eg: '\u2A9A', + Egrave: '\u00C8', + egrave: '\u00E8', + egs: '\u2A96', + egsdot: '\u2A98', + el: '\u2A99', + Element: '\u2208', + elinters: '\u23E7', + ell: '\u2113', + els: '\u2A95', + elsdot: '\u2A97', + Emacr: '\u0112', + emacr: '\u0113', + empty: '\u2205', + emptyset: '\u2205', + EmptySmallSquare: '\u25FB', + emptyv: '\u2205', + EmptyVerySmallSquare: '\u25AB', + emsp: '\u2003', + emsp13: '\u2004', + emsp14: '\u2005', + ENG: '\u014A', + eng: '\u014B', + ensp: '\u2002', + Eogon: '\u0118', + eogon: '\u0119', + Eopf: '\uD835\uDD3C', + eopf: '\uD835\uDD56', + epar: '\u22D5', + eparsl: '\u29E3', + eplus: '\u2A71', + epsi: '\u03B5', + Epsilon: '\u0395', + epsilon: '\u03B5', + epsiv: '\u03F5', + eqcirc: '\u2256', + eqcolon: '\u2255', + eqsim: '\u2242', + eqslantgtr: '\u2A96', + eqslantless: '\u2A95', + Equal: '\u2A75', + equals: '\u003D', + EqualTilde: '\u2242', + equest: '\u225F', + Equilibrium: '\u21CC', + equiv: '\u2261', + equivDD: '\u2A78', + eqvparsl: '\u29E5', + erarr: '\u2971', + erDot: '\u2253', + Escr: '\u2130', + escr: '\u212F', + esdot: '\u2250', + Esim: '\u2A73', + esim: '\u2242', + Eta: '\u0397', + eta: '\u03B7', + ETH: '\u00D0', + eth: '\u00F0', + Euml: '\u00CB', + euml: '\u00EB', + euro: '\u20AC', + excl: '\u0021', + exist: '\u2203', + Exists: '\u2203', + expectation: '\u2130', + ExponentialE: '\u2147', + exponentiale: '\u2147', + fallingdotseq: '\u2252', + Fcy: '\u0424', + fcy: '\u0444', + female: '\u2640', + ffilig: '\uFB03', + fflig: '\uFB00', + ffllig: '\uFB04', + Ffr: '\uD835\uDD09', + ffr: '\uD835\uDD23', + filig: '\uFB01', + FilledSmallSquare: '\u25FC', + FilledVerySmallSquare: '\u25AA', + fjlig: '\u0066\u006A', + flat: '\u266D', + fllig: '\uFB02', + fltns: '\u25B1', + fnof: '\u0192', + Fopf: '\uD835\uDD3D', + fopf: '\uD835\uDD57', + ForAll: '\u2200', + forall: '\u2200', + fork: '\u22D4', + forkv: '\u2AD9', + Fouriertrf: '\u2131', + fpartint: '\u2A0D', + frac12: '\u00BD', + frac13: '\u2153', + frac14: '\u00BC', + frac15: '\u2155', + frac16: '\u2159', + frac18: '\u215B', + frac23: '\u2154', + frac25: '\u2156', + frac34: '\u00BE', + frac35: '\u2157', + frac38: '\u215C', + frac45: '\u2158', + frac56: '\u215A', + frac58: '\u215D', + frac78: '\u215E', + frasl: '\u2044', + frown: '\u2322', + Fscr: '\u2131', + fscr: '\uD835\uDCBB', + gacute: '\u01F5', + Gamma: '\u0393', + gamma: '\u03B3', + Gammad: '\u03DC', + gammad: '\u03DD', + gap: '\u2A86', + Gbreve: '\u011E', + gbreve: '\u011F', + Gcedil: '\u0122', + Gcirc: '\u011C', + gcirc: '\u011D', + Gcy: '\u0413', + gcy: '\u0433', + Gdot: '\u0120', + gdot: '\u0121', + gE: '\u2267', + ge: '\u2265', + gEl: '\u2A8C', + gel: '\u22DB', + geq: '\u2265', + geqq: '\u2267', + geqslant: '\u2A7E', + ges: '\u2A7E', + gescc: '\u2AA9', + gesdot: '\u2A80', + gesdoto: '\u2A82', + gesdotol: '\u2A84', + gesl: '\u22DB\uFE00', + gesles: '\u2A94', + Gfr: '\uD835\uDD0A', + gfr: '\uD835\uDD24', + Gg: '\u22D9', + gg: '\u226B', + ggg: '\u22D9', + gimel: '\u2137', + GJcy: '\u0403', + gjcy: '\u0453', + gl: '\u2277', + gla: '\u2AA5', + glE: '\u2A92', + glj: '\u2AA4', + gnap: '\u2A8A', + gnapprox: '\u2A8A', + gnE: '\u2269', + gne: '\u2A88', + gneq: '\u2A88', + gneqq: '\u2269', + gnsim: '\u22E7', + Gopf: '\uD835\uDD3E', + gopf: '\uD835\uDD58', + grave: '\u0060', + GreaterEqual: '\u2265', + GreaterEqualLess: '\u22DB', + GreaterFullEqual: '\u2267', + GreaterGreater: '\u2AA2', + GreaterLess: '\u2277', + GreaterSlantEqual: '\u2A7E', + GreaterTilde: '\u2273', + Gscr: '\uD835\uDCA2', + gscr: '\u210A', + gsim: '\u2273', + gsime: '\u2A8E', + gsiml: '\u2A90', + Gt: '\u226B', + GT: '\u003E', + gt: '\u003E', + gtcc: '\u2AA7', + gtcir: '\u2A7A', + gtdot: '\u22D7', + gtlPar: '\u2995', + gtquest: '\u2A7C', + gtrapprox: '\u2A86', + gtrarr: '\u2978', + gtrdot: '\u22D7', + gtreqless: '\u22DB', + gtreqqless: '\u2A8C', + gtrless: '\u2277', + gtrsim: '\u2273', + gvertneqq: '\u2269\uFE00', + gvnE: '\u2269\uFE00', + Hacek: '\u02C7', + hairsp: '\u200A', + half: '\u00BD', + hamilt: '\u210B', + HARDcy: '\u042A', + hardcy: '\u044A', + hArr: '\u21D4', + harr: '\u2194', + harrcir: '\u2948', + harrw: '\u21AD', + Hat: '\u005E', + hbar: '\u210F', + Hcirc: '\u0124', + hcirc: '\u0125', + hearts: '\u2665', + heartsuit: '\u2665', + hellip: '\u2026', + hercon: '\u22B9', + Hfr: '\u210C', + hfr: '\uD835\uDD25', + HilbertSpace: '\u210B', + hksearow: '\u2925', + hkswarow: '\u2926', + hoarr: '\u21FF', + homtht: '\u223B', + hookleftarrow: '\u21A9', + hookrightarrow: '\u21AA', + Hopf: '\u210D', + hopf: '\uD835\uDD59', + horbar: '\u2015', + HorizontalLine: '\u2500', + Hscr: '\u210B', + hscr: '\uD835\uDCBD', + hslash: '\u210F', + Hstrok: '\u0126', + hstrok: '\u0127', + HumpDownHump: '\u224E', + HumpEqual: '\u224F', + hybull: '\u2043', + hyphen: '\u2010', + Iacute: '\u00CD', + iacute: '\u00ED', + ic: '\u2063', + Icirc: '\u00CE', + icirc: '\u00EE', + Icy: '\u0418', + icy: '\u0438', + Idot: '\u0130', + IEcy: '\u0415', + iecy: '\u0435', + iexcl: '\u00A1', + iff: '\u21D4', + Ifr: '\u2111', + ifr: '\uD835\uDD26', + Igrave: '\u00CC', + igrave: '\u00EC', + ii: '\u2148', + iiiint: '\u2A0C', + iiint: '\u222D', + iinfin: '\u29DC', + iiota: '\u2129', + IJlig: '\u0132', + ijlig: '\u0133', + Im: '\u2111', + Imacr: '\u012A', + imacr: '\u012B', + image: '\u2111', + ImaginaryI: '\u2148', + imagline: '\u2110', + imagpart: '\u2111', + imath: '\u0131', + imof: '\u22B7', + imped: '\u01B5', + Implies: '\u21D2', + in: '\u2208', + incare: '\u2105', + infin: '\u221E', + infintie: '\u29DD', + inodot: '\u0131', + Int: '\u222C', + int: '\u222B', + intcal: '\u22BA', + integers: '\u2124', + Integral: '\u222B', + intercal: '\u22BA', + Intersection: '\u22C2', + intlarhk: '\u2A17', + intprod: '\u2A3C', + InvisibleComma: '\u2063', + InvisibleTimes: '\u2062', + IOcy: '\u0401', + iocy: '\u0451', + Iogon: '\u012E', + iogon: '\u012F', + Iopf: '\uD835\uDD40', + iopf: '\uD835\uDD5A', + Iota: '\u0399', + iota: '\u03B9', + iprod: '\u2A3C', + iquest: '\u00BF', + Iscr: '\u2110', + iscr: '\uD835\uDCBE', + isin: '\u2208', + isindot: '\u22F5', + isinE: '\u22F9', + isins: '\u22F4', + isinsv: '\u22F3', + isinv: '\u2208', + it: '\u2062', + Itilde: '\u0128', + itilde: '\u0129', + Iukcy: '\u0406', + iukcy: '\u0456', + Iuml: '\u00CF', + iuml: '\u00EF', + Jcirc: '\u0134', + jcirc: '\u0135', + Jcy: '\u0419', + jcy: '\u0439', + Jfr: '\uD835\uDD0D', + jfr: '\uD835\uDD27', + jmath: '\u0237', + Jopf: '\uD835\uDD41', + jopf: '\uD835\uDD5B', + Jscr: '\uD835\uDCA5', + jscr: '\uD835\uDCBF', + Jsercy: '\u0408', + jsercy: '\u0458', + Jukcy: '\u0404', + jukcy: '\u0454', + Kappa: '\u039A', + kappa: '\u03BA', + kappav: '\u03F0', + Kcedil: '\u0136', + kcedil: '\u0137', + Kcy: '\u041A', + kcy: '\u043A', + Kfr: '\uD835\uDD0E', + kfr: '\uD835\uDD28', + kgreen: '\u0138', + KHcy: '\u0425', + khcy: '\u0445', + KJcy: '\u040C', + kjcy: '\u045C', + Kopf: '\uD835\uDD42', + kopf: '\uD835\uDD5C', + Kscr: '\uD835\uDCA6', + kscr: '\uD835\uDCC0', + lAarr: '\u21DA', + Lacute: '\u0139', + lacute: '\u013A', + laemptyv: '\u29B4', + lagran: '\u2112', + Lambda: '\u039B', + lambda: '\u03BB', + Lang: '\u27EA', + lang: '\u27E8', + langd: '\u2991', + langle: '\u27E8', + lap: '\u2A85', + Laplacetrf: '\u2112', + laquo: '\u00AB', + Larr: '\u219E', + lArr: '\u21D0', + larr: '\u2190', + larrb: '\u21E4', + larrbfs: '\u291F', + larrfs: '\u291D', + larrhk: '\u21A9', + larrlp: '\u21AB', + larrpl: '\u2939', + larrsim: '\u2973', + larrtl: '\u21A2', + lat: '\u2AAB', + lAtail: '\u291B', + latail: '\u2919', + late: '\u2AAD', + lates: '\u2AAD\uFE00', + lBarr: '\u290E', + lbarr: '\u290C', + lbbrk: '\u2772', + lbrace: '\u007B', + lbrack: '\u005B', + lbrke: '\u298B', + lbrksld: '\u298F', + lbrkslu: '\u298D', + Lcaron: '\u013D', + lcaron: '\u013E', + Lcedil: '\u013B', + lcedil: '\u013C', + lceil: '\u2308', + lcub: '\u007B', + Lcy: '\u041B', + lcy: '\u043B', + ldca: '\u2936', + ldquo: '\u201C', + ldquor: '\u201E', + ldrdhar: '\u2967', + ldrushar: '\u294B', + ldsh: '\u21B2', + lE: '\u2266', + le: '\u2264', + LeftAngleBracket: '\u27E8', + LeftArrow: '\u2190', + Leftarrow: '\u21D0', + leftarrow: '\u2190', + LeftArrowBar: '\u21E4', + LeftArrowRightArrow: '\u21C6', + leftarrowtail: '\u21A2', + LeftCeiling: '\u2308', + LeftDoubleBracket: '\u27E6', + LeftDownTeeVector: '\u2961', + LeftDownVector: '\u21C3', + LeftDownVectorBar: '\u2959', + LeftFloor: '\u230A', + leftharpoondown: '\u21BD', + leftharpoonup: '\u21BC', + leftleftarrows: '\u21C7', + LeftRightArrow: '\u2194', + Leftrightarrow: '\u21D4', + leftrightarrow: '\u2194', + leftrightarrows: '\u21C6', + leftrightharpoons: '\u21CB', + leftrightsquigarrow: '\u21AD', + LeftRightVector: '\u294E', + LeftTee: '\u22A3', + LeftTeeArrow: '\u21A4', + LeftTeeVector: '\u295A', + leftthreetimes: '\u22CB', + LeftTriangle: '\u22B2', + LeftTriangleBar: '\u29CF', + LeftTriangleEqual: '\u22B4', + LeftUpDownVector: '\u2951', + LeftUpTeeVector: '\u2960', + LeftUpVector: '\u21BF', + LeftUpVectorBar: '\u2958', + LeftVector: '\u21BC', + LeftVectorBar: '\u2952', + lEg: '\u2A8B', + leg: '\u22DA', + leq: '\u2264', + leqq: '\u2266', + leqslant: '\u2A7D', + les: '\u2A7D', + lescc: '\u2AA8', + lesdot: '\u2A7F', + lesdoto: '\u2A81', + lesdotor: '\u2A83', + lesg: '\u22DA\uFE00', + lesges: '\u2A93', + lessapprox: '\u2A85', + lessdot: '\u22D6', + lesseqgtr: '\u22DA', + lesseqqgtr: '\u2A8B', + LessEqualGreater: '\u22DA', + LessFullEqual: '\u2266', + LessGreater: '\u2276', + lessgtr: '\u2276', + LessLess: '\u2AA1', + lesssim: '\u2272', + LessSlantEqual: '\u2A7D', + LessTilde: '\u2272', + lfisht: '\u297C', + lfloor: '\u230A', + Lfr: '\uD835\uDD0F', + lfr: '\uD835\uDD29', + lg: '\u2276', + lgE: '\u2A91', + lHar: '\u2962', + lhard: '\u21BD', + lharu: '\u21BC', + lharul: '\u296A', + lhblk: '\u2584', + LJcy: '\u0409', + ljcy: '\u0459', + Ll: '\u22D8', + ll: '\u226A', + llarr: '\u21C7', + llcorner: '\u231E', + Lleftarrow: '\u21DA', + llhard: '\u296B', + lltri: '\u25FA', + Lmidot: '\u013F', + lmidot: '\u0140', + lmoust: '\u23B0', + lmoustache: '\u23B0', + lnap: '\u2A89', + lnapprox: '\u2A89', + lnE: '\u2268', + lne: '\u2A87', + lneq: '\u2A87', + lneqq: '\u2268', + lnsim: '\u22E6', + loang: '\u27EC', + loarr: '\u21FD', + lobrk: '\u27E6', + LongLeftArrow: '\u27F5', + Longleftarrow: '\u27F8', + longleftarrow: '\u27F5', + LongLeftRightArrow: '\u27F7', + Longleftrightarrow: '\u27FA', + longleftrightarrow: '\u27F7', + longmapsto: '\u27FC', + LongRightArrow: '\u27F6', + Longrightarrow: '\u27F9', + longrightarrow: '\u27F6', + looparrowleft: '\u21AB', + looparrowright: '\u21AC', + lopar: '\u2985', + Lopf: '\uD835\uDD43', + lopf: '\uD835\uDD5D', + loplus: '\u2A2D', + lotimes: '\u2A34', + lowast: '\u2217', + lowbar: '\u005F', + LowerLeftArrow: '\u2199', + LowerRightArrow: '\u2198', + loz: '\u25CA', + lozenge: '\u25CA', + lozf: '\u29EB', + lpar: '\u0028', + lparlt: '\u2993', + lrarr: '\u21C6', + lrcorner: '\u231F', + lrhar: '\u21CB', + lrhard: '\u296D', + lrm: '\u200E', + lrtri: '\u22BF', + lsaquo: '\u2039', + Lscr: '\u2112', + lscr: '\uD835\uDCC1', + Lsh: '\u21B0', + lsh: '\u21B0', + lsim: '\u2272', + lsime: '\u2A8D', + lsimg: '\u2A8F', + lsqb: '\u005B', + lsquo: '\u2018', + lsquor: '\u201A', + Lstrok: '\u0141', + lstrok: '\u0142', + Lt: '\u226A', + LT: '\u003C', + lt: '\u003C', + ltcc: '\u2AA6', + ltcir: '\u2A79', + ltdot: '\u22D6', + lthree: '\u22CB', + ltimes: '\u22C9', + ltlarr: '\u2976', + ltquest: '\u2A7B', + ltri: '\u25C3', + ltrie: '\u22B4', + ltrif: '\u25C2', + ltrPar: '\u2996', + lurdshar: '\u294A', + luruhar: '\u2966', + lvertneqq: '\u2268\uFE00', + lvnE: '\u2268\uFE00', + macr: '\u00AF', + male: '\u2642', + malt: '\u2720', + maltese: '\u2720', + Map: '\u2905', + map: '\u21A6', + mapsto: '\u21A6', + mapstodown: '\u21A7', + mapstoleft: '\u21A4', + mapstoup: '\u21A5', + marker: '\u25AE', + mcomma: '\u2A29', + Mcy: '\u041C', + mcy: '\u043C', + mdash: '\u2014', + mDDot: '\u223A', + measuredangle: '\u2221', + MediumSpace: '\u205F', + Mellintrf: '\u2133', + Mfr: '\uD835\uDD10', + mfr: '\uD835\uDD2A', + mho: '\u2127', + micro: '\u00B5', + mid: '\u2223', + midast: '\u002A', + midcir: '\u2AF0', + middot: '\u00B7', + minus: '\u2212', + minusb: '\u229F', + minusd: '\u2238', + minusdu: '\u2A2A', + MinusPlus: '\u2213', + mlcp: '\u2ADB', + mldr: '\u2026', + mnplus: '\u2213', + models: '\u22A7', + Mopf: '\uD835\uDD44', + mopf: '\uD835\uDD5E', + mp: '\u2213', + Mscr: '\u2133', + mscr: '\uD835\uDCC2', + mstpos: '\u223E', + Mu: '\u039C', + mu: '\u03BC', + multimap: '\u22B8', + mumap: '\u22B8', + nabla: '\u2207', + Nacute: '\u0143', + nacute: '\u0144', + nang: '\u2220\u20D2', + nap: '\u2249', + napE: '\u2A70\u0338', + napid: '\u224B\u0338', + napos: '\u0149', + napprox: '\u2249', + natur: '\u266E', + natural: '\u266E', + naturals: '\u2115', + nbsp: '\u00A0', + nbump: '\u224E\u0338', + nbumpe: '\u224F\u0338', + ncap: '\u2A43', + Ncaron: '\u0147', + ncaron: '\u0148', + Ncedil: '\u0145', + ncedil: '\u0146', + ncong: '\u2247', + ncongdot: '\u2A6D\u0338', + ncup: '\u2A42', + Ncy: '\u041D', + ncy: '\u043D', + ndash: '\u2013', + ne: '\u2260', + nearhk: '\u2924', + neArr: '\u21D7', + nearr: '\u2197', + nearrow: '\u2197', + nedot: '\u2250\u0338', + NegativeMediumSpace: '\u200B', + NegativeThickSpace: '\u200B', + NegativeThinSpace: '\u200B', + NegativeVeryThinSpace: '\u200B', + nequiv: '\u2262', + nesear: '\u2928', + nesim: '\u2242\u0338', + NestedGreaterGreater: '\u226B', + NestedLessLess: '\u226A', + NewLine: '\u000A', + nexist: '\u2204', + nexists: '\u2204', + Nfr: '\uD835\uDD11', + nfr: '\uD835\uDD2B', + ngE: '\u2267\u0338', + nge: '\u2271', + ngeq: '\u2271', + ngeqq: '\u2267\u0338', + ngeqslant: '\u2A7E\u0338', + nges: '\u2A7E\u0338', + nGg: '\u22D9\u0338', + ngsim: '\u2275', + nGt: '\u226B\u20D2', + ngt: '\u226F', + ngtr: '\u226F', + nGtv: '\u226B\u0338', + nhArr: '\u21CE', + nharr: '\u21AE', + nhpar: '\u2AF2', + ni: '\u220B', + nis: '\u22FC', + nisd: '\u22FA', + niv: '\u220B', + NJcy: '\u040A', + njcy: '\u045A', + nlArr: '\u21CD', + nlarr: '\u219A', + nldr: '\u2025', + nlE: '\u2266\u0338', + nle: '\u2270', + nLeftarrow: '\u21CD', + nleftarrow: '\u219A', + nLeftrightarrow: '\u21CE', + nleftrightarrow: '\u21AE', + nleq: '\u2270', + nleqq: '\u2266\u0338', + nleqslant: '\u2A7D\u0338', + nles: '\u2A7D\u0338', + nless: '\u226E', + nLl: '\u22D8\u0338', + nlsim: '\u2274', + nLt: '\u226A\u20D2', + nlt: '\u226E', + nltri: '\u22EA', + nltrie: '\u22EC', + nLtv: '\u226A\u0338', + nmid: '\u2224', + NoBreak: '\u2060', + NonBreakingSpace: '\u00A0', + Nopf: '\u2115', + nopf: '\uD835\uDD5F', + Not: '\u2AEC', + not: '\u00AC', + NotCongruent: '\u2262', + NotCupCap: '\u226D', + NotDoubleVerticalBar: '\u2226', + NotElement: '\u2209', + NotEqual: '\u2260', + NotEqualTilde: '\u2242\u0338', + NotExists: '\u2204', + NotGreater: '\u226F', + NotGreaterEqual: '\u2271', + NotGreaterFullEqual: '\u2267\u0338', + NotGreaterGreater: '\u226B\u0338', + NotGreaterLess: '\u2279', + NotGreaterSlantEqual: '\u2A7E\u0338', + NotGreaterTilde: '\u2275', + NotHumpDownHump: '\u224E\u0338', + NotHumpEqual: '\u224F\u0338', + notin: '\u2209', + notindot: '\u22F5\u0338', + notinE: '\u22F9\u0338', + notinva: '\u2209', + notinvb: '\u22F7', + notinvc: '\u22F6', + NotLeftTriangle: '\u22EA', + NotLeftTriangleBar: '\u29CF\u0338', + NotLeftTriangleEqual: '\u22EC', + NotLess: '\u226E', + NotLessEqual: '\u2270', + NotLessGreater: '\u2278', + NotLessLess: '\u226A\u0338', + NotLessSlantEqual: '\u2A7D\u0338', + NotLessTilde: '\u2274', + NotNestedGreaterGreater: '\u2AA2\u0338', + NotNestedLessLess: '\u2AA1\u0338', + notni: '\u220C', + notniva: '\u220C', + notnivb: '\u22FE', + notnivc: '\u22FD', + NotPrecedes: '\u2280', + NotPrecedesEqual: '\u2AAF\u0338', + NotPrecedesSlantEqual: '\u22E0', + NotReverseElement: '\u220C', + NotRightTriangle: '\u22EB', + NotRightTriangleBar: '\u29D0\u0338', + NotRightTriangleEqual: '\u22ED', + NotSquareSubset: '\u228F\u0338', + NotSquareSubsetEqual: '\u22E2', + NotSquareSuperset: '\u2290\u0338', + NotSquareSupersetEqual: '\u22E3', + NotSubset: '\u2282\u20D2', + NotSubsetEqual: '\u2288', + NotSucceeds: '\u2281', + NotSucceedsEqual: '\u2AB0\u0338', + NotSucceedsSlantEqual: '\u22E1', + NotSucceedsTilde: '\u227F\u0338', + NotSuperset: '\u2283\u20D2', + NotSupersetEqual: '\u2289', + NotTilde: '\u2241', + NotTildeEqual: '\u2244', + NotTildeFullEqual: '\u2247', + NotTildeTilde: '\u2249', + NotVerticalBar: '\u2224', + npar: '\u2226', + nparallel: '\u2226', + nparsl: '\u2AFD\u20E5', + npart: '\u2202\u0338', + npolint: '\u2A14', + npr: '\u2280', + nprcue: '\u22E0', + npre: '\u2AAF\u0338', + nprec: '\u2280', + npreceq: '\u2AAF\u0338', + nrArr: '\u21CF', + nrarr: '\u219B', + nrarrc: '\u2933\u0338', + nrarrw: '\u219D\u0338', + nRightarrow: '\u21CF', + nrightarrow: '\u219B', + nrtri: '\u22EB', + nrtrie: '\u22ED', + nsc: '\u2281', + nsccue: '\u22E1', + nsce: '\u2AB0\u0338', + Nscr: '\uD835\uDCA9', + nscr: '\uD835\uDCC3', + nshortmid: '\u2224', + nshortparallel: '\u2226', + nsim: '\u2241', + nsime: '\u2244', + nsimeq: '\u2244', + nsmid: '\u2224', + nspar: '\u2226', + nsqsube: '\u22E2', + nsqsupe: '\u22E3', + nsub: '\u2284', + nsubE: '\u2AC5\u0338', + nsube: '\u2288', + nsubset: '\u2282\u20D2', + nsubseteq: '\u2288', + nsubseteqq: '\u2AC5\u0338', + nsucc: '\u2281', + nsucceq: '\u2AB0\u0338', + nsup: '\u2285', + nsupE: '\u2AC6\u0338', + nsupe: '\u2289', + nsupset: '\u2283\u20D2', + nsupseteq: '\u2289', + nsupseteqq: '\u2AC6\u0338', + ntgl: '\u2279', + Ntilde: '\u00D1', + ntilde: '\u00F1', + ntlg: '\u2278', + ntriangleleft: '\u22EA', + ntrianglelefteq: '\u22EC', + ntriangleright: '\u22EB', + ntrianglerighteq: '\u22ED', + Nu: '\u039D', + nu: '\u03BD', + num: '\u0023', + numero: '\u2116', + numsp: '\u2007', + nvap: '\u224D\u20D2', + nVDash: '\u22AF', + nVdash: '\u22AE', + nvDash: '\u22AD', + nvdash: '\u22AC', + nvge: '\u2265\u20D2', + nvgt: '\u003E\u20D2', + nvHarr: '\u2904', + nvinfin: '\u29DE', + nvlArr: '\u2902', + nvle: '\u2264\u20D2', + nvlt: '\u003C\u20D2', + nvltrie: '\u22B4\u20D2', + nvrArr: '\u2903', + nvrtrie: '\u22B5\u20D2', + nvsim: '\u223C\u20D2', + nwarhk: '\u2923', + nwArr: '\u21D6', + nwarr: '\u2196', + nwarrow: '\u2196', + nwnear: '\u2927', + Oacute: '\u00D3', + oacute: '\u00F3', + oast: '\u229B', + ocir: '\u229A', + Ocirc: '\u00D4', + ocirc: '\u00F4', + Ocy: '\u041E', + ocy: '\u043E', + odash: '\u229D', + Odblac: '\u0150', + odblac: '\u0151', + odiv: '\u2A38', + odot: '\u2299', + odsold: '\u29BC', + OElig: '\u0152', + oelig: '\u0153', + ofcir: '\u29BF', + Ofr: '\uD835\uDD12', + ofr: '\uD835\uDD2C', + ogon: '\u02DB', + Ograve: '\u00D2', + ograve: '\u00F2', + ogt: '\u29C1', + ohbar: '\u29B5', + ohm: '\u03A9', + oint: '\u222E', + olarr: '\u21BA', + olcir: '\u29BE', + olcross: '\u29BB', + oline: '\u203E', + olt: '\u29C0', + Omacr: '\u014C', + omacr: '\u014D', + Omega: '\u03A9', + omega: '\u03C9', + Omicron: '\u039F', + omicron: '\u03BF', + omid: '\u29B6', + ominus: '\u2296', + Oopf: '\uD835\uDD46', + oopf: '\uD835\uDD60', + opar: '\u29B7', + OpenCurlyDoubleQuote: '\u201C', + OpenCurlyQuote: '\u2018', + operp: '\u29B9', + oplus: '\u2295', + Or: '\u2A54', + or: '\u2228', + orarr: '\u21BB', + ord: '\u2A5D', + order: '\u2134', + orderof: '\u2134', + ordf: '\u00AA', + ordm: '\u00BA', + origof: '\u22B6', + oror: '\u2A56', + orslope: '\u2A57', + orv: '\u2A5B', + oS: '\u24C8', + Oscr: '\uD835\uDCAA', + oscr: '\u2134', + Oslash: '\u00D8', + oslash: '\u00F8', + osol: '\u2298', + Otilde: '\u00D5', + otilde: '\u00F5', + Otimes: '\u2A37', + otimes: '\u2297', + otimesas: '\u2A36', + Ouml: '\u00D6', + ouml: '\u00F6', + ovbar: '\u233D', + OverBar: '\u203E', + OverBrace: '\u23DE', + OverBracket: '\u23B4', + OverParenthesis: '\u23DC', + par: '\u2225', + para: '\u00B6', + parallel: '\u2225', + parsim: '\u2AF3', + parsl: '\u2AFD', + part: '\u2202', + PartialD: '\u2202', + Pcy: '\u041F', + pcy: '\u043F', + percnt: '\u0025', + period: '\u002E', + permil: '\u2030', + perp: '\u22A5', + pertenk: '\u2031', + Pfr: '\uD835\uDD13', + pfr: '\uD835\uDD2D', + Phi: '\u03A6', + phi: '\u03C6', + phiv: '\u03D5', + phmmat: '\u2133', + phone: '\u260E', + Pi: '\u03A0', + pi: '\u03C0', + pitchfork: '\u22D4', + piv: '\u03D6', + planck: '\u210F', + planckh: '\u210E', + plankv: '\u210F', + plus: '\u002B', + plusacir: '\u2A23', + plusb: '\u229E', + pluscir: '\u2A22', + plusdo: '\u2214', + plusdu: '\u2A25', + pluse: '\u2A72', + PlusMinus: '\u00B1', + plusmn: '\u00B1', + plussim: '\u2A26', + plustwo: '\u2A27', + pm: '\u00B1', + Poincareplane: '\u210C', + pointint: '\u2A15', + Popf: '\u2119', + popf: '\uD835\uDD61', + pound: '\u00A3', + Pr: '\u2ABB', + pr: '\u227A', + prap: '\u2AB7', + prcue: '\u227C', + prE: '\u2AB3', + pre: '\u2AAF', + prec: '\u227A', + precapprox: '\u2AB7', + preccurlyeq: '\u227C', + Precedes: '\u227A', + PrecedesEqual: '\u2AAF', + PrecedesSlantEqual: '\u227C', + PrecedesTilde: '\u227E', + preceq: '\u2AAF', + precnapprox: '\u2AB9', + precneqq: '\u2AB5', + precnsim: '\u22E8', + precsim: '\u227E', + Prime: '\u2033', + prime: '\u2032', + primes: '\u2119', + prnap: '\u2AB9', + prnE: '\u2AB5', + prnsim: '\u22E8', + prod: '\u220F', + Product: '\u220F', + profalar: '\u232E', + profline: '\u2312', + profsurf: '\u2313', + prop: '\u221D', + Proportion: '\u2237', + Proportional: '\u221D', + propto: '\u221D', + prsim: '\u227E', + prurel: '\u22B0', + Pscr: '\uD835\uDCAB', + pscr: '\uD835\uDCC5', + Psi: '\u03A8', + psi: '\u03C8', + puncsp: '\u2008', + Qfr: '\uD835\uDD14', + qfr: '\uD835\uDD2E', + qint: '\u2A0C', + Qopf: '\u211A', + qopf: '\uD835\uDD62', + qprime: '\u2057', + Qscr: '\uD835\uDCAC', + qscr: '\uD835\uDCC6', + quaternions: '\u210D', + quatint: '\u2A16', + quest: '\u003F', + questeq: '\u225F', + QUOT: '\u0022', + quot: '\u0022', + rAarr: '\u21DB', + race: '\u223D\u0331', + Racute: '\u0154', + racute: '\u0155', + radic: '\u221A', + raemptyv: '\u29B3', + Rang: '\u27EB', + rang: '\u27E9', + rangd: '\u2992', + range: '\u29A5', + rangle: '\u27E9', + raquo: '\u00BB', + Rarr: '\u21A0', + rArr: '\u21D2', + rarr: '\u2192', + rarrap: '\u2975', + rarrb: '\u21E5', + rarrbfs: '\u2920', + rarrc: '\u2933', + rarrfs: '\u291E', + rarrhk: '\u21AA', + rarrlp: '\u21AC', + rarrpl: '\u2945', + rarrsim: '\u2974', + Rarrtl: '\u2916', + rarrtl: '\u21A3', + rarrw: '\u219D', + rAtail: '\u291C', + ratail: '\u291A', + ratio: '\u2236', + rationals: '\u211A', + RBarr: '\u2910', + rBarr: '\u290F', + rbarr: '\u290D', + rbbrk: '\u2773', + rbrace: '\u007D', + rbrack: '\u005D', + rbrke: '\u298C', + rbrksld: '\u298E', + rbrkslu: '\u2990', + Rcaron: '\u0158', + rcaron: '\u0159', + Rcedil: '\u0156', + rcedil: '\u0157', + rceil: '\u2309', + rcub: '\u007D', + Rcy: '\u0420', + rcy: '\u0440', + rdca: '\u2937', + rdldhar: '\u2969', + rdquo: '\u201D', + rdquor: '\u201D', + rdsh: '\u21B3', + Re: '\u211C', + real: '\u211C', + realine: '\u211B', + realpart: '\u211C', + reals: '\u211D', + rect: '\u25AD', + REG: '\u00AE', + reg: '\u00AE', + ReverseElement: '\u220B', + ReverseEquilibrium: '\u21CB', + ReverseUpEquilibrium: '\u296F', + rfisht: '\u297D', + rfloor: '\u230B', + Rfr: '\u211C', + rfr: '\uD835\uDD2F', + rHar: '\u2964', + rhard: '\u21C1', + rharu: '\u21C0', + rharul: '\u296C', + Rho: '\u03A1', + rho: '\u03C1', + rhov: '\u03F1', + RightAngleBracket: '\u27E9', + RightArrow: '\u2192', + Rightarrow: '\u21D2', + rightarrow: '\u2192', + RightArrowBar: '\u21E5', + RightArrowLeftArrow: '\u21C4', + rightarrowtail: '\u21A3', + RightCeiling: '\u2309', + RightDoubleBracket: '\u27E7', + RightDownTeeVector: '\u295D', + RightDownVector: '\u21C2', + RightDownVectorBar: '\u2955', + RightFloor: '\u230B', + rightharpoondown: '\u21C1', + rightharpoonup: '\u21C0', + rightleftarrows: '\u21C4', + rightleftharpoons: '\u21CC', + rightrightarrows: '\u21C9', + rightsquigarrow: '\u219D', + RightTee: '\u22A2', + RightTeeArrow: '\u21A6', + RightTeeVector: '\u295B', + rightthreetimes: '\u22CC', + RightTriangle: '\u22B3', + RightTriangleBar: '\u29D0', + RightTriangleEqual: '\u22B5', + RightUpDownVector: '\u294F', + RightUpTeeVector: '\u295C', + RightUpVector: '\u21BE', + RightUpVectorBar: '\u2954', + RightVector: '\u21C0', + RightVectorBar: '\u2953', + ring: '\u02DA', + risingdotseq: '\u2253', + rlarr: '\u21C4', + rlhar: '\u21CC', + rlm: '\u200F', + rmoust: '\u23B1', + rmoustache: '\u23B1', + rnmid: '\u2AEE', + roang: '\u27ED', + roarr: '\u21FE', + robrk: '\u27E7', + ropar: '\u2986', + Ropf: '\u211D', + ropf: '\uD835\uDD63', + roplus: '\u2A2E', + rotimes: '\u2A35', + RoundImplies: '\u2970', + rpar: '\u0029', + rpargt: '\u2994', + rppolint: '\u2A12', + rrarr: '\u21C9', + Rrightarrow: '\u21DB', + rsaquo: '\u203A', + Rscr: '\u211B', + rscr: '\uD835\uDCC7', + Rsh: '\u21B1', + rsh: '\u21B1', + rsqb: '\u005D', + rsquo: '\u2019', + rsquor: '\u2019', + rthree: '\u22CC', + rtimes: '\u22CA', + rtri: '\u25B9', + rtrie: '\u22B5', + rtrif: '\u25B8', + rtriltri: '\u29CE', + RuleDelayed: '\u29F4', + ruluhar: '\u2968', + rx: '\u211E', + Sacute: '\u015A', + sacute: '\u015B', + sbquo: '\u201A', + Sc: '\u2ABC', + sc: '\u227B', + scap: '\u2AB8', + Scaron: '\u0160', + scaron: '\u0161', + sccue: '\u227D', + scE: '\u2AB4', + sce: '\u2AB0', + Scedil: '\u015E', + scedil: '\u015F', + Scirc: '\u015C', + scirc: '\u015D', + scnap: '\u2ABA', + scnE: '\u2AB6', + scnsim: '\u22E9', + scpolint: '\u2A13', + scsim: '\u227F', + Scy: '\u0421', + scy: '\u0441', + sdot: '\u22C5', + sdotb: '\u22A1', + sdote: '\u2A66', + searhk: '\u2925', + seArr: '\u21D8', + searr: '\u2198', + searrow: '\u2198', + sect: '\u00A7', + semi: '\u003B', + seswar: '\u2929', + setminus: '\u2216', + setmn: '\u2216', + sext: '\u2736', + Sfr: '\uD835\uDD16', + sfr: '\uD835\uDD30', + sfrown: '\u2322', + sharp: '\u266F', + SHCHcy: '\u0429', + shchcy: '\u0449', + SHcy: '\u0428', + shcy: '\u0448', + ShortDownArrow: '\u2193', + ShortLeftArrow: '\u2190', + shortmid: '\u2223', + shortparallel: '\u2225', + ShortRightArrow: '\u2192', + ShortUpArrow: '\u2191', + shy: '\u00AD', + Sigma: '\u03A3', + sigma: '\u03C3', + sigmaf: '\u03C2', + sigmav: '\u03C2', + sim: '\u223C', + simdot: '\u2A6A', + sime: '\u2243', + simeq: '\u2243', + simg: '\u2A9E', + simgE: '\u2AA0', + siml: '\u2A9D', + simlE: '\u2A9F', + simne: '\u2246', + simplus: '\u2A24', + simrarr: '\u2972', + slarr: '\u2190', + SmallCircle: '\u2218', + smallsetminus: '\u2216', + smashp: '\u2A33', + smeparsl: '\u29E4', + smid: '\u2223', + smile: '\u2323', + smt: '\u2AAA', + smte: '\u2AAC', + smtes: '\u2AAC\uFE00', + SOFTcy: '\u042C', + softcy: '\u044C', + sol: '\u002F', + solb: '\u29C4', + solbar: '\u233F', + Sopf: '\uD835\uDD4A', + sopf: '\uD835\uDD64', + spades: '\u2660', + spadesuit: '\u2660', + spar: '\u2225', + sqcap: '\u2293', + sqcaps: '\u2293\uFE00', + sqcup: '\u2294', + sqcups: '\u2294\uFE00', + Sqrt: '\u221A', + sqsub: '\u228F', + sqsube: '\u2291', + sqsubset: '\u228F', + sqsubseteq: '\u2291', + sqsup: '\u2290', + sqsupe: '\u2292', + sqsupset: '\u2290', + sqsupseteq: '\u2292', + squ: '\u25A1', + Square: '\u25A1', + square: '\u25A1', + SquareIntersection: '\u2293', + SquareSubset: '\u228F', + SquareSubsetEqual: '\u2291', + SquareSuperset: '\u2290', + SquareSupersetEqual: '\u2292', + SquareUnion: '\u2294', + squarf: '\u25AA', + squf: '\u25AA', + srarr: '\u2192', + Sscr: '\uD835\uDCAE', + sscr: '\uD835\uDCC8', + ssetmn: '\u2216', + ssmile: '\u2323', + sstarf: '\u22C6', + Star: '\u22C6', + star: '\u2606', + starf: '\u2605', + straightepsilon: '\u03F5', + straightphi: '\u03D5', + strns: '\u00AF', + Sub: '\u22D0', + sub: '\u2282', + subdot: '\u2ABD', + subE: '\u2AC5', + sube: '\u2286', + subedot: '\u2AC3', + submult: '\u2AC1', + subnE: '\u2ACB', + subne: '\u228A', + subplus: '\u2ABF', + subrarr: '\u2979', + Subset: '\u22D0', + subset: '\u2282', + subseteq: '\u2286', + subseteqq: '\u2AC5', + SubsetEqual: '\u2286', + subsetneq: '\u228A', + subsetneqq: '\u2ACB', + subsim: '\u2AC7', + subsub: '\u2AD5', + subsup: '\u2AD3', + succ: '\u227B', + succapprox: '\u2AB8', + succcurlyeq: '\u227D', + Succeeds: '\u227B', + SucceedsEqual: '\u2AB0', + SucceedsSlantEqual: '\u227D', + SucceedsTilde: '\u227F', + succeq: '\u2AB0', + succnapprox: '\u2ABA', + succneqq: '\u2AB6', + succnsim: '\u22E9', + succsim: '\u227F', + SuchThat: '\u220B', + Sum: '\u2211', + sum: '\u2211', + sung: '\u266A', + Sup: '\u22D1', + sup: '\u2283', + sup1: '\u00B9', + sup2: '\u00B2', + sup3: '\u00B3', + supdot: '\u2ABE', + supdsub: '\u2AD8', + supE: '\u2AC6', + supe: '\u2287', + supedot: '\u2AC4', + Superset: '\u2283', + SupersetEqual: '\u2287', + suphsol: '\u27C9', + suphsub: '\u2AD7', + suplarr: '\u297B', + supmult: '\u2AC2', + supnE: '\u2ACC', + supne: '\u228B', + supplus: '\u2AC0', + Supset: '\u22D1', + supset: '\u2283', + supseteq: '\u2287', + supseteqq: '\u2AC6', + supsetneq: '\u228B', + supsetneqq: '\u2ACC', + supsim: '\u2AC8', + supsub: '\u2AD4', + supsup: '\u2AD6', + swarhk: '\u2926', + swArr: '\u21D9', + swarr: '\u2199', + swarrow: '\u2199', + swnwar: '\u292A', + szlig: '\u00DF', + Tab: '\u0009', + target: '\u2316', + Tau: '\u03A4', + tau: '\u03C4', + tbrk: '\u23B4', + Tcaron: '\u0164', + tcaron: '\u0165', + Tcedil: '\u0162', + tcedil: '\u0163', + Tcy: '\u0422', + tcy: '\u0442', + tdot: '\u20DB', + telrec: '\u2315', + Tfr: '\uD835\uDD17', + tfr: '\uD835\uDD31', + there4: '\u2234', + Therefore: '\u2234', + therefore: '\u2234', + Theta: '\u0398', + theta: '\u03B8', + thetasym: '\u03D1', + thetav: '\u03D1', + thickapprox: '\u2248', + thicksim: '\u223C', + ThickSpace: '\u205F\u200A', + thinsp: '\u2009', + ThinSpace: '\u2009', + thkap: '\u2248', + thksim: '\u223C', + THORN: '\u00DE', + thorn: '\u00FE', + Tilde: '\u223C', + tilde: '\u02DC', + TildeEqual: '\u2243', + TildeFullEqual: '\u2245', + TildeTilde: '\u2248', + times: '\u00D7', + timesb: '\u22A0', + timesbar: '\u2A31', + timesd: '\u2A30', + tint: '\u222D', + toea: '\u2928', + top: '\u22A4', + topbot: '\u2336', + topcir: '\u2AF1', + Topf: '\uD835\uDD4B', + topf: '\uD835\uDD65', + topfork: '\u2ADA', + tosa: '\u2929', + tprime: '\u2034', + TRADE: '\u2122', + trade: '\u2122', + triangle: '\u25B5', + triangledown: '\u25BF', + triangleleft: '\u25C3', + trianglelefteq: '\u22B4', + triangleq: '\u225C', + triangleright: '\u25B9', + trianglerighteq: '\u22B5', + tridot: '\u25EC', + trie: '\u225C', + triminus: '\u2A3A', + TripleDot: '\u20DB', + triplus: '\u2A39', + trisb: '\u29CD', + tritime: '\u2A3B', + trpezium: '\u23E2', + Tscr: '\uD835\uDCAF', + tscr: '\uD835\uDCC9', + TScy: '\u0426', + tscy: '\u0446', + TSHcy: '\u040B', + tshcy: '\u045B', + Tstrok: '\u0166', + tstrok: '\u0167', + twixt: '\u226C', + twoheadleftarrow: '\u219E', + twoheadrightarrow: '\u21A0', + Uacute: '\u00DA', + uacute: '\u00FA', + Uarr: '\u219F', + uArr: '\u21D1', + uarr: '\u2191', + Uarrocir: '\u2949', + Ubrcy: '\u040E', + ubrcy: '\u045E', + Ubreve: '\u016C', + ubreve: '\u016D', + Ucirc: '\u00DB', + ucirc: '\u00FB', + Ucy: '\u0423', + ucy: '\u0443', + udarr: '\u21C5', + Udblac: '\u0170', + udblac: '\u0171', + udhar: '\u296E', + ufisht: '\u297E', + Ufr: '\uD835\uDD18', + ufr: '\uD835\uDD32', + Ugrave: '\u00D9', + ugrave: '\u00F9', + uHar: '\u2963', + uharl: '\u21BF', + uharr: '\u21BE', + uhblk: '\u2580', + ulcorn: '\u231C', + ulcorner: '\u231C', + ulcrop: '\u230F', + ultri: '\u25F8', + Umacr: '\u016A', + umacr: '\u016B', + uml: '\u00A8', + UnderBar: '\u005F', + UnderBrace: '\u23DF', + UnderBracket: '\u23B5', + UnderParenthesis: '\u23DD', + Union: '\u22C3', + UnionPlus: '\u228E', + Uogon: '\u0172', + uogon: '\u0173', + Uopf: '\uD835\uDD4C', + uopf: '\uD835\uDD66', + UpArrow: '\u2191', + Uparrow: '\u21D1', + uparrow: '\u2191', + UpArrowBar: '\u2912', + UpArrowDownArrow: '\u21C5', + UpDownArrow: '\u2195', + Updownarrow: '\u21D5', + updownarrow: '\u2195', + UpEquilibrium: '\u296E', + upharpoonleft: '\u21BF', + upharpoonright: '\u21BE', + uplus: '\u228E', + UpperLeftArrow: '\u2196', + UpperRightArrow: '\u2197', + Upsi: '\u03D2', + upsi: '\u03C5', + upsih: '\u03D2', + Upsilon: '\u03A5', + upsilon: '\u03C5', + UpTee: '\u22A5', + UpTeeArrow: '\u21A5', + upuparrows: '\u21C8', + urcorn: '\u231D', + urcorner: '\u231D', + urcrop: '\u230E', + Uring: '\u016E', + uring: '\u016F', + urtri: '\u25F9', + Uscr: '\uD835\uDCB0', + uscr: '\uD835\uDCCA', + utdot: '\u22F0', + Utilde: '\u0168', + utilde: '\u0169', + utri: '\u25B5', + utrif: '\u25B4', + uuarr: '\u21C8', + Uuml: '\u00DC', + uuml: '\u00FC', + uwangle: '\u29A7', + vangrt: '\u299C', + varepsilon: '\u03F5', + varkappa: '\u03F0', + varnothing: '\u2205', + varphi: '\u03D5', + varpi: '\u03D6', + varpropto: '\u221D', + vArr: '\u21D5', + varr: '\u2195', + varrho: '\u03F1', + varsigma: '\u03C2', + varsubsetneq: '\u228A\uFE00', + varsubsetneqq: '\u2ACB\uFE00', + varsupsetneq: '\u228B\uFE00', + varsupsetneqq: '\u2ACC\uFE00', + vartheta: '\u03D1', + vartriangleleft: '\u22B2', + vartriangleright: '\u22B3', + Vbar: '\u2AEB', + vBar: '\u2AE8', + vBarv: '\u2AE9', + Vcy: '\u0412', + vcy: '\u0432', + VDash: '\u22AB', + Vdash: '\u22A9', + vDash: '\u22A8', + vdash: '\u22A2', + Vdashl: '\u2AE6', + Vee: '\u22C1', + vee: '\u2228', + veebar: '\u22BB', + veeeq: '\u225A', + vellip: '\u22EE', + Verbar: '\u2016', + verbar: '\u007C', + Vert: '\u2016', + vert: '\u007C', + VerticalBar: '\u2223', + VerticalLine: '\u007C', + VerticalSeparator: '\u2758', + VerticalTilde: '\u2240', + VeryThinSpace: '\u200A', + Vfr: '\uD835\uDD19', + vfr: '\uD835\uDD33', + vltri: '\u22B2', + vnsub: '\u2282\u20D2', + vnsup: '\u2283\u20D2', + Vopf: '\uD835\uDD4D', + vopf: '\uD835\uDD67', + vprop: '\u221D', + vrtri: '\u22B3', + Vscr: '\uD835\uDCB1', + vscr: '\uD835\uDCCB', + vsubnE: '\u2ACB\uFE00', + vsubne: '\u228A\uFE00', + vsupnE: '\u2ACC\uFE00', + vsupne: '\u228B\uFE00', + Vvdash: '\u22AA', + vzigzag: '\u299A', + Wcirc: '\u0174', + wcirc: '\u0175', + wedbar: '\u2A5F', + Wedge: '\u22C0', + wedge: '\u2227', + wedgeq: '\u2259', + weierp: '\u2118', + Wfr: '\uD835\uDD1A', + wfr: '\uD835\uDD34', + Wopf: '\uD835\uDD4E', + wopf: '\uD835\uDD68', + wp: '\u2118', + wr: '\u2240', + wreath: '\u2240', + Wscr: '\uD835\uDCB2', + wscr: '\uD835\uDCCC', + xcap: '\u22C2', + xcirc: '\u25EF', + xcup: '\u22C3', + xdtri: '\u25BD', + Xfr: '\uD835\uDD1B', + xfr: '\uD835\uDD35', + xhArr: '\u27FA', + xharr: '\u27F7', + Xi: '\u039E', + xi: '\u03BE', + xlArr: '\u27F8', + xlarr: '\u27F5', + xmap: '\u27FC', + xnis: '\u22FB', + xodot: '\u2A00', + Xopf: '\uD835\uDD4F', + xopf: '\uD835\uDD69', + xoplus: '\u2A01', + xotime: '\u2A02', + xrArr: '\u27F9', + xrarr: '\u27F6', + Xscr: '\uD835\uDCB3', + xscr: '\uD835\uDCCD', + xsqcup: '\u2A06', + xuplus: '\u2A04', + xutri: '\u25B3', + xvee: '\u22C1', + xwedge: '\u22C0', + Yacute: '\u00DD', + yacute: '\u00FD', + YAcy: '\u042F', + yacy: '\u044F', + Ycirc: '\u0176', + ycirc: '\u0177', + Ycy: '\u042B', + ycy: '\u044B', + yen: '\u00A5', + Yfr: '\uD835\uDD1C', + yfr: '\uD835\uDD36', + YIcy: '\u0407', + yicy: '\u0457', + Yopf: '\uD835\uDD50', + yopf: '\uD835\uDD6A', + Yscr: '\uD835\uDCB4', + yscr: '\uD835\uDCCE', + YUcy: '\u042E', + yucy: '\u044E', + Yuml: '\u0178', + yuml: '\u00FF', + Zacute: '\u0179', + zacute: '\u017A', + Zcaron: '\u017D', + zcaron: '\u017E', + Zcy: '\u0417', + zcy: '\u0437', + Zdot: '\u017B', + zdot: '\u017C', + zeetrf: '\u2128', + ZeroWidthSpace: '\u200B', + Zeta: '\u0396', + zeta: '\u03B6', + Zfr: '\u2128', + zfr: '\uD835\uDD37', + ZHcy: '\u0416', + zhcy: '\u0436', + zigrarr: '\u21DD', + Zopf: '\u2124', + zopf: '\uD835\uDD6B', + Zscr: '\uD835\uDCB5', + zscr: '\uD835\uDCCF', + zwj: '\u200D', + zwnj: '\u200C', +}); + +/** + * @deprecated use `HTML_ENTITIES` instead + * @see HTML_ENTITIES + */ +exports.entityMap = exports.HTML_ENTITIES; + +},{"./conventions":2}],6:[function(require,module,exports){ +var dom = require('./dom') +exports.DOMImplementation = dom.DOMImplementation +exports.XMLSerializer = dom.XMLSerializer +exports.DOMParser = require('./dom-parser').DOMParser + +},{"./dom":4,"./dom-parser":3}],7:[function(require,module,exports){ +var NAMESPACE = require("./conventions").NAMESPACE; + +//[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] +//[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] +//[5] Name ::= NameStartChar (NameChar)* +var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF +var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"); +var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$'); +//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ +//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') + +//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE +//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE +var S_TAG = 0;//tag name offerring +var S_ATTR = 1;//attr name offerring +var S_ATTR_SPACE=2;//attr name end and space offer +var S_EQ = 3;//=space? +var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only) +var S_ATTR_END = 5;//attr value end and no space(quot end) +var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer) +var S_TAG_CLOSE = 7;//closed el + +/** + * Creates an error that will not be caught by XMLReader aka the SAX parser. + * + * @param {string} message + * @param {any?} locator Optional, can provide details about the location in the source + * @constructor + */ +function ParseError(message, locator) { + this.message = message + this.locator = locator + if(Error.captureStackTrace) Error.captureStackTrace(this, ParseError); +} +ParseError.prototype = new Error(); +ParseError.prototype.name = ParseError.name + +function XMLReader(){ + +} + +XMLReader.prototype = { + parse:function(source,defaultNSMap,entityMap){ + var domBuilder = this.domBuilder; + domBuilder.startDocument(); + _copy(defaultNSMap ,defaultNSMap = {}) + parse(source,defaultNSMap,entityMap, + domBuilder,this.errorHandler); + domBuilder.endDocument(); + } +} +function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){ + function fixedFromCharCode(code) { + // String.prototype.fromCharCode does not supports + // > 2 bytes unicode chars directly + if (code > 0xffff) { + code -= 0x10000; + var surrogate1 = 0xd800 + (code >> 10) + , surrogate2 = 0xdc00 + (code & 0x3ff); + + return String.fromCharCode(surrogate1, surrogate2); + } else { + return String.fromCharCode(code); + } + } + function entityReplacer(a){ + var k = a.slice(1,-1); + if (Object.hasOwnProperty.call(entityMap, k)) { + return entityMap[k]; + }else if(k.charAt(0) === '#'){ + return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x'))) + }else{ + errorHandler.error('entity not found:'+a); + return a; + } + } + function appendText(end){//has some bugs + if(end>start){ + var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer); + locator&&position(start); + domBuilder.characters(xt,0,end-start); + start = end + } + } + function position(p,m){ + while(p>=lineEnd && (m = linePattern.exec(source))){ + lineStart = m.index; + lineEnd = lineStart + m[0].length; + locator.lineNumber++; + //console.log('line++:',locator,startPos,endPos) + } + locator.columnNumber = p-lineStart+1; + } + var lineStart = 0; + var lineEnd = 0; + var linePattern = /.*(?:\r\n?|\n)|.*$/g + var locator = domBuilder.locator; + + var parseStack = [{currentNSMap:defaultNSMapCopy}] + var closeMap = {}; + var start = 0; + while(true){ + try{ + var tagStart = source.indexOf('<',start); + if(tagStart<0){ + if(!source.substr(start).match(/^\s*$/)){ + var doc = domBuilder.doc; + var text = doc.createTextNode(source.substr(start)); + doc.appendChild(text); + domBuilder.currentElement = text; + } + return; + } + if(tagStart>start){ + appendText(tagStart); + } + switch(source.charAt(tagStart+1)){ + case '/': + var end = source.indexOf('>',tagStart+3); + var tagName = source.substring(tagStart + 2, end).replace(/[ \t\n\r]+$/g, ''); + var config = parseStack.pop(); + if(end<0){ + + tagName = source.substring(tagStart+2).replace(/[\s<].*/,''); + errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName); + end = tagStart+1+tagName.length; + }else if(tagName.match(/\s + locator&&position(tagStart); + end = parseInstruction(source,tagStart,domBuilder); + break; + case '!':// start){ + start = end; + }else{ + //TODO: 这里有可能sax回退,有位置错误风险 + appendText(Math.max(tagStart,start)+1); + } + } +} +function copyLocator(f,t){ + t.lineNumber = f.lineNumber; + t.columnNumber = f.columnNumber; + return t; +} + +/** + * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack); + * @return end of the elementStartPart(end of elementEndPart for selfClosed el) + */ +function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){ + + /** + * @param {string} qname + * @param {string} value + * @param {number} startIndex + */ + function addAttribute(qname, value, startIndex) { + if (el.attributeNames.hasOwnProperty(qname)) { + errorHandler.fatalError('Attribute ' + qname + ' redefined') + } + el.addValue( + qname, + // @see https://www.w3.org/TR/xml/#AVNormalize + // since the xmldom sax parser does not "interpret" DTD the following is not implemented: + // - recursive replacement of (DTD) entity references + // - trimming and collapsing multiple spaces into a single one for attributes that are not of type CDATA + value.replace(/[\t\n\r]/g, ' ').replace(/&#?\w+;/g, entityReplacer), + startIndex + ) + } + var attrName; + var value; + var p = ++start; + var s = S_TAG;//status + while(true){ + var c = source.charAt(p); + switch(c){ + case '=': + if(s === S_ATTR){//attrName + attrName = source.slice(start,p); + s = S_EQ; + }else if(s === S_ATTR_SPACE){ + s = S_EQ; + }else{ + //fatalError: equal must after attrName or space after attrName + throw new Error('attribute equal must after attrName'); // No known test case + } + break; + case '\'': + case '"': + if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE + ){//equal + if(s === S_ATTR){ + errorHandler.warning('attribute value must after "="') + attrName = source.slice(start,p) + } + start = p+1; + p = source.indexOf(c,start) + if(p>0){ + value = source.slice(start, p); + addAttribute(attrName, value, start-1); + s = S_ATTR_END; + }else{ + //fatalError: no end quot match + throw new Error('attribute value no end \''+c+'\' match'); + } + }else if(s == S_ATTR_NOQUOT_VALUE){ + value = source.slice(start, p); + addAttribute(attrName, value, start); + errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!'); + start = p+1; + s = S_ATTR_END + }else{ + //fatalError: no equal before + throw new Error('attribute value must after "="'); // No known test case + } + break; + case '/': + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p)); + case S_ATTR_END: + case S_TAG_SPACE: + case S_TAG_CLOSE: + s =S_TAG_CLOSE; + el.closed = true; + case S_ATTR_NOQUOT_VALUE: + case S_ATTR: + break; + case S_ATTR_SPACE: + el.closed = true; + break; + //case S_EQ: + default: + throw new Error("attribute invalid close char('/')") // No known test case + } + break; + case ''://end document + errorHandler.error('unexpected end of input'); + if(s == S_TAG){ + el.setTagName(source.slice(start,p)); + } + return p; + case '>': + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p)); + case S_ATTR_END: + case S_TAG_SPACE: + case S_TAG_CLOSE: + break;//normal + case S_ATTR_NOQUOT_VALUE://Compatible state + case S_ATTR: + value = source.slice(start,p); + if(value.slice(-1) === '/'){ + el.closed = true; + value = value.slice(0,-1) + } + case S_ATTR_SPACE: + if(s === S_ATTR_SPACE){ + value = attrName; + } + if(s == S_ATTR_NOQUOT_VALUE){ + errorHandler.warning('attribute "'+value+'" missed quot(")!'); + addAttribute(attrName, value, start) + }else{ + if(!NAMESPACE.isHTML(currentNSMap['']) || !value.match(/^(?:disabled|checked|selected)$/i)){ + errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!') + } + addAttribute(value, value, start) + } + break; + case S_EQ: + throw new Error('attribute value missed!!'); + } +// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) + return p; + /*xml space '\x20' | #x9 | #xD | #xA; */ + case '\u0080': + c = ' '; + default: + if(c<= ' '){//space + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p));//tagName + s = S_TAG_SPACE; + break; + case S_ATTR: + attrName = source.slice(start,p) + s = S_ATTR_SPACE; + break; + case S_ATTR_NOQUOT_VALUE: + var value = source.slice(start, p); + errorHandler.warning('attribute "'+value+'" missed quot(")!!'); + addAttribute(attrName, value, start) + case S_ATTR_END: + s = S_TAG_SPACE; + break; + //case S_TAG_SPACE: + //case S_EQ: + //case S_ATTR_SPACE: + // void();break; + //case S_TAG_CLOSE: + //ignore warning + } + }else{//not space +//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE +//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE + switch(s){ + //case S_TAG:void();break; + //case S_ATTR:void();break; + //case S_ATTR_NOQUOT_VALUE:void();break; + case S_ATTR_SPACE: + var tagName = el.tagName; + if (!NAMESPACE.isHTML(currentNSMap['']) || !attrName.match(/^(?:disabled|checked|selected)$/i)) { + errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!') + } + addAttribute(attrName, attrName, start); + start = p; + s = S_ATTR; + break; + case S_ATTR_END: + errorHandler.warning('attribute space is required"'+attrName+'"!!') + case S_TAG_SPACE: + s = S_ATTR; + start = p; + break; + case S_EQ: + s = S_ATTR_NOQUOT_VALUE; + start = p; + break; + case S_TAG_CLOSE: + throw new Error("elements closed character '/' and '>' must be connected to"); + } + } + }//end outer switch + //console.log('p++',p) + p++; + } +} +/** + * @return true if has new namespace define + */ +function appendElement(el,domBuilder,currentNSMap){ + var tagName = el.tagName; + var localNSMap = null; + //var currentNSMap = parseStack[parseStack.length-1].currentNSMap; + var i = el.length; + while(i--){ + var a = el[i]; + var qName = a.qName; + var value = a.value; + var nsp = qName.indexOf(':'); + if(nsp>0){ + var prefix = a.prefix = qName.slice(0,nsp); + var localName = qName.slice(nsp+1); + var nsPrefix = prefix === 'xmlns' && localName + }else{ + localName = qName; + prefix = null + nsPrefix = qName === 'xmlns' && '' + } + //can not set prefix,because prefix !== '' + a.localName = localName ; + //prefix == null for no ns prefix attribute + if(nsPrefix !== false){//hack!! + if(localNSMap == null){ + localNSMap = {} + //console.log(currentNSMap,0) + _copy(currentNSMap,currentNSMap={}) + //console.log(currentNSMap,1) + } + currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value; + a.uri = NAMESPACE.XMLNS + domBuilder.startPrefixMapping(nsPrefix, value) + } + } + var i = el.length; + while(i--){ + a = el[i]; + var prefix = a.prefix; + if(prefix){//no prefix attribute has no namespace + if(prefix === 'xml'){ + a.uri = NAMESPACE.XML; + }if(prefix !== 'xmlns'){ + a.uri = currentNSMap[prefix || ''] + + //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)} + } + } + } + var nsp = tagName.indexOf(':'); + if(nsp>0){ + prefix = el.prefix = tagName.slice(0,nsp); + localName = el.localName = tagName.slice(nsp+1); + }else{ + prefix = null;//important!! + localName = el.localName = tagName; + } + //no prefix element has default namespace + var ns = el.uri = currentNSMap[prefix || '']; + domBuilder.startElement(ns,localName,tagName,el); + //endPrefixMapping and startPrefixMapping have not any help for dom builder + //localNSMap = null + if(el.closed){ + domBuilder.endElement(ns,localName,tagName); + if(localNSMap){ + for (prefix in localNSMap) { + if (Object.prototype.hasOwnProperty.call(localNSMap, prefix)) { + domBuilder.endPrefixMapping(prefix); + } + } + } + }else{ + el.currentNSMap = currentNSMap; + el.localNSMap = localNSMap; + //parseStack.push(el); + return true; + } +} +function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){ + if(/^(?:script|textarea)$/i.test(tagName)){ + var elEndStart = source.indexOf('',elStartEnd); + var text = source.substring(elStartEnd+1,elEndStart); + if(/[&<]/.test(text)){ + if(/^script$/i.test(tagName)){ + //if(!/\]\]>/.test(text)){ + //lexHandler.startCDATA(); + domBuilder.characters(text,0,text.length); + //lexHandler.endCDATA(); + return elEndStart; + //} + }//}else{//text area + text = text.replace(/&#?\w+;/g,entityReplacer); + domBuilder.characters(text,0,text.length); + return elEndStart; + //} + + } + } + return elStartEnd+1; +} +function fixSelfClosed(source,elStartEnd,tagName,closeMap){ + //if(tagName in closeMap){ + var pos = closeMap[tagName]; + if(pos == null){ + //console.log(tagName) + pos = source.lastIndexOf('') + if(pos',start+4); + //append comment source.substring(4,end)//, + * and raw CDATA nodes. + * + * @param {Element} node + * @returns {Boolean} + * @api private + */ + +function shouldIgnoreNode (node) { + return node.nodeType === TEXT_NODE + || node.nodeType === COMMENT_NODE + || node.nodeType === CDATA_NODE; +} + +/** + * Check if the node is empty. Some plist file has such node: + * + * this node shoud be ignored. + * + * @see https://github.com/TooTallNate/plist.js/issues/66 + * @param {Element} node + * @returns {Boolean} + * @api private + */ +function isEmptyNode(node){ + if(!node.childNodes || node.childNodes.length === 0) { + return true; + } else { + return false; + } +} + +function invariant(test, message) { + if (!test) { + throw new Error(message); + } +} + +/** + * Parses a Plist XML string. Returns an Object. + * + * @param {String} xml - the XML String to decode + * @returns {Mixed} the decoded value from the Plist XML + * @api public + */ + +function parse (xml) { + var doc = new DOMParser().parseFromString(xml); + invariant( + doc.documentElement.nodeName === 'plist', + 'malformed document. First element should be ' + ); + var plist = parsePlistXML(doc.documentElement); + + // the root node gets interpreted as an Array, + // so pull out the inner data first + if (plist.length == 1) plist = plist[0]; + + return plist; +} + +/** + * Convert an XML based plist document into a JSON representation. + * + * @param {Object} xml_node - current XML node in the plist + * @returns {Mixed} built up JSON object + * @api private + */ + +function parsePlistXML (node) { + var i, new_obj, key, val, new_arr, res, counter, type; + + if (!node) + return null; + + if (node.nodeName === 'plist') { + new_arr = []; + if (isEmptyNode(node)) { + return new_arr; + } + for (i=0; i < node.childNodes.length; i++) { + if (!shouldIgnoreNode(node.childNodes[i])) { + new_arr.push( parsePlistXML(node.childNodes[i])); + } + } + return new_arr; + } else if (node.nodeName === 'dict') { + new_obj = {}; + key = null; + counter = 0; + if (isEmptyNode(node)) { + return new_obj; + } + for (i=0; i < node.childNodes.length; i++) { + if (shouldIgnoreNode(node.childNodes[i])) continue; + if (counter % 2 === 0) { + invariant( + node.childNodes[i].nodeName === 'key', + 'Missing key while parsing .' + ); + key = parsePlistXML(node.childNodes[i]); + } else { + invariant( + node.childNodes[i].nodeName !== 'key', + 'Unexpected key "' + + parsePlistXML(node.childNodes[i]) + + '" while parsing .' + ); + new_obj[key] = parsePlistXML(node.childNodes[i]); + } + counter += 1; + } + if (counter % 2 === 1) { + new_obj[key] = ''; + } + + return new_obj; + + } else if (node.nodeName === 'array') { + new_arr = []; + if (isEmptyNode(node)) { + return new_arr; + } + for (i=0; i < node.childNodes.length; i++) { + if (!shouldIgnoreNode(node.childNodes[i])) { + res = parsePlistXML(node.childNodes[i]); + if (null != res) new_arr.push(res); + } + } + return new_arr; + + } else if (node.nodeName === '#text') { + // TODO: what should we do with text types? (CDATA sections) + + } else if (node.nodeName === 'key') { + if (isEmptyNode(node)) { + return ''; + } + + invariant( + node.childNodes[0].nodeValue !== '__proto__', + '__proto__ keys can lead to prototype pollution. More details on CVE-2022-22912' + ); + + return node.childNodes[0].nodeValue; + } else if (node.nodeName === 'string') { + res = ''; + if (isEmptyNode(node)) { + return res; + } + for (i=0; i < node.childNodes.length; i++) { + var type = node.childNodes[i].nodeType; + if (type === TEXT_NODE || type === CDATA_NODE) { + res += node.childNodes[i].nodeValue; + } + } + return res; + + } else if (node.nodeName === 'integer') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as integer.' + ); + return parseInt(node.childNodes[0].nodeValue, 10); + + } else if (node.nodeName === 'real') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as real.' + ); + res = ''; + for (i=0; i < node.childNodes.length; i++) { + if (node.childNodes[i].nodeType === TEXT_NODE) { + res += node.childNodes[i].nodeValue; + } + } + return parseFloat(res); + + } else if (node.nodeName === 'data') { + res = ''; + if (isEmptyNode(node)) { + return Buffer.from(res, 'base64'); + } + for (i=0; i < node.childNodes.length; i++) { + if (node.childNodes[i].nodeType === TEXT_NODE) { + res += node.childNodes[i].nodeValue.replace(/\s+/g, ''); + } + } + return Buffer.from(res, 'base64'); + + } else if (node.nodeName === 'date') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as Date.' + ) + return new Date(node.childNodes[0].nodeValue); + + } else if (node.nodeName === 'null') { + return null; + + } else if (node.nodeName === 'true') { + return true; + + } else if (node.nodeName === 'false') { + return false; + } else { + throw new Error('Invalid PLIST tag ' + node.nodeName); + } +} + +}).call(this)}).call(this,require("buffer").Buffer) +},{"./xmldom/dom-parser":4,"buffer":9}],4:[function(require,module,exports){ +function DOMParser(options){ + this.options = options ||{locator:{}}; +} + +DOMParser.prototype.parseFromString = function(source,mimeType){ + var options = this.options; + var sax = new XMLReader(); + var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler + var errorHandler = options.errorHandler; + var locator = options.locator; + var defaultNSMap = options.xmlns||{}; + var isHTML = /\/x?html?$/.test(mimeType);//mimeType.toLowerCase().indexOf('html') > -1; + var entityMap = isHTML?htmlEntity.entityMap:{'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"}; + if(locator){ + domBuilder.setDocumentLocator(locator) + } + + sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator); + sax.domBuilder = options.domBuilder || domBuilder; + if(isHTML){ + defaultNSMap['']= 'http://www.w3.org/1999/xhtml'; + } + defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace'; + if(source && typeof source === 'string'){ + sax.parse(source,defaultNSMap,entityMap); + }else{ + sax.errorHandler.error("invalid doc source"); + } + return domBuilder.doc; +} +function buildErrorHandler(errorImpl,domBuilder,locator){ + if(!errorImpl){ + if(domBuilder instanceof DOMHandler){ + return domBuilder; + } + errorImpl = domBuilder ; + } + var errorHandler = {} + var isCallback = errorImpl instanceof Function; + locator = locator||{} + function build(key){ + var fn = errorImpl[key]; + if(!fn && isCallback){ + fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl; + } + errorHandler[key] = fn && function(msg){ + fn('[xmldom '+key+']\t'+msg+_locator(locator)); + }||function(){}; + } + build('warning'); + build('error'); + build('fatalError'); + return errorHandler; +} + +//console.log('#\n\n\n\n\n\n\n####') +/** + * +ContentHandler+ErrorHandler + * +LexicalHandler+EntityResolver2 + * -DeclHandler-DTDHandler + * + * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler + * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2 + * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html + */ +function DOMHandler() { + this.cdata = false; +} +function position(locator,node){ + node.lineNumber = locator.lineNumber; + node.columnNumber = locator.columnNumber; +} +/** + * @see org.xml.sax.ContentHandler#startDocument + * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html + */ +DOMHandler.prototype = { + startDocument : function() { + this.doc = new DOMImplementation().createDocument(null, null, null); + if (this.locator) { + this.doc.documentURI = this.locator.systemId; + } + }, + startElement:function(namespaceURI, localName, qName, attrs) { + var doc = this.doc; + var el = doc.createElementNS(namespaceURI, qName||localName); + var len = attrs.length; + appendElement(this, el); + this.currentElement = el; + + this.locator && position(this.locator,el) + for (var i = 0 ; i < len; i++) { + var namespaceURI = attrs.getURI(i); + var value = attrs.getValue(i); + var qName = attrs.getQName(i); + var attr = doc.createAttributeNS(namespaceURI, qName); + this.locator &&position(attrs.getLocator(i),attr); + attr.value = attr.nodeValue = value; + el.setAttributeNode(attr) + } + }, + endElement:function(namespaceURI, localName, qName) { + var current = this.currentElement + var tagName = current.tagName; + this.currentElement = current.parentNode; + }, + startPrefixMapping:function(prefix, uri) { + }, + endPrefixMapping:function(prefix) { + }, + processingInstruction:function(target, data) { + var ins = this.doc.createProcessingInstruction(target, data); + this.locator && position(this.locator,ins) + appendElement(this, ins); + }, + ignorableWhitespace:function(ch, start, length) { + }, + characters:function(chars, start, length) { + chars = _toString.apply(this,arguments) + //console.log(chars) + if(chars){ + if (this.cdata) { + var charNode = this.doc.createCDATASection(chars); + } else { + var charNode = this.doc.createTextNode(chars); + } + if(this.currentElement){ + this.currentElement.appendChild(charNode); + }else if(/^\s*$/.test(chars)){ + this.doc.appendChild(charNode); + //process xml + } + this.locator && position(this.locator,charNode) + } + }, + skippedEntity:function(name) { + }, + endDocument:function() { + this.doc.normalize(); + }, + setDocumentLocator:function (locator) { + if(this.locator = locator){// && !('lineNumber' in locator)){ + locator.lineNumber = 0; + } + }, + //LexicalHandler + comment:function(chars, start, length) { + chars = _toString.apply(this,arguments) + var comm = this.doc.createComment(chars); + this.locator && position(this.locator,comm) + appendElement(this, comm); + }, + + startCDATA:function() { + //used in characters() methods + this.cdata = true; + }, + endCDATA:function() { + this.cdata = false; + }, + + startDTD:function(name, publicId, systemId) { + var impl = this.doc.implementation; + if (impl && impl.createDocumentType) { + var dt = impl.createDocumentType(name, publicId, systemId); + this.locator && position(this.locator,dt) + appendElement(this, dt); + } + }, + /** + * @see org.xml.sax.ErrorHandler + * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html + */ + warning:function(error) { + console.warn('[xmldom warning]\t'+error,_locator(this.locator)); + }, + error:function(error) { + console.error('[xmldom error]\t'+error,_locator(this.locator)); + }, + fatalError:function(error) { + throw new ParseError(error, this.locator); + } +} +function _locator(l){ + if(l){ + return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']' + } +} +function _toString(chars,start,length){ + if(typeof chars == 'string'){ + return chars.substr(start,length) + }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") + if(chars.length >= start+length || start){ + return new java.lang.String(chars,start,length)+''; + } + return chars; + } +} + +/* + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html + * used method of org.xml.sax.ext.LexicalHandler: + * #comment(chars, start, length) + * #startCDATA() + * #endCDATA() + * #startDTD(name, publicId, systemId) + * + * + * IGNORED method of org.xml.sax.ext.LexicalHandler: + * #endDTD() + * #startEntity(name) + * #endEntity(name) + * + * + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html + * IGNORED method of org.xml.sax.ext.DeclHandler + * #attributeDecl(eName, aName, type, mode, value) + * #elementDecl(name, model) + * #externalEntityDecl(name, publicId, systemId) + * #internalEntityDecl(name, value) + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html + * IGNORED method of org.xml.sax.EntityResolver2 + * #resolveEntity(String name,String publicId,String baseURI,String systemId) + * #resolveEntity(publicId, systemId) + * #getExternalSubset(name, baseURI) + * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html + * IGNORED method of org.xml.sax.DTDHandler + * #notationDecl(name, publicId, systemId) {}; + * #unparsedEntityDecl(name, publicId, systemId, notationName) {}; + */ +"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){ + DOMHandler.prototype[key] = function(){return null} +}) + +/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */ +function appendElement (hander,node) { + if (!hander.currentElement) { + hander.doc.appendChild(node); + } else { + hander.currentElement.appendChild(node); + } +}//appendChild and setAttributeNS are preformance key + +//if(typeof require == 'function'){ +var htmlEntity = require('./entities'); +var sax = require('./sax'); +var XMLReader = sax.XMLReader; +var ParseError = sax.ParseError; +var DOMImplementation = exports.DOMImplementation = require('./dom').DOMImplementation; +exports.XMLSerializer = require('./dom').XMLSerializer ; +exports.DOMParser = DOMParser; +exports.__DOMHandler = DOMHandler; +//} + +},{"./dom":5,"./entities":6,"./sax":7}],5:[function(require,module,exports){ +function copy(src,dest){ + for(var p in src){ + dest[p] = src[p]; + } +} +/** +^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));? +^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));? + */ +function _extends(Class,Super){ + var pt = Class.prototype; + if(!(pt instanceof Super)){ + function t(){}; + t.prototype = Super.prototype; + t = new t(); + copy(pt,t); + Class.prototype = pt = t; + } + if(pt.constructor != Class){ + if(typeof Class != 'function'){ + console.error("unknow Class:"+Class) + } + pt.constructor = Class + } +} +var htmlns = 'http://www.w3.org/1999/xhtml' ; +// Node Types +var NodeType = {} +var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1; +var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2; +var TEXT_NODE = NodeType.TEXT_NODE = 3; +var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4; +var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5; +var ENTITY_NODE = NodeType.ENTITY_NODE = 6; +var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7; +var COMMENT_NODE = NodeType.COMMENT_NODE = 8; +var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9; +var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10; +var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11; +var NOTATION_NODE = NodeType.NOTATION_NODE = 12; + +// ExceptionCode +var ExceptionCode = {} +var ExceptionMessage = {}; +var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = ((ExceptionMessage[1]="Index size error"),1); +var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = ((ExceptionMessage[2]="DOMString size error"),2); +var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = ((ExceptionMessage[3]="Hierarchy request error"),3); +var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = ((ExceptionMessage[4]="Wrong document"),4); +var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = ((ExceptionMessage[5]="Invalid character"),5); +var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = ((ExceptionMessage[6]="No data allowed"),6); +var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7); +var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = ((ExceptionMessage[8]="Not found"),8); +var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = ((ExceptionMessage[9]="Not supported"),9); +var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = ((ExceptionMessage[10]="Attribute in use"),10); +//level2 +var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = ((ExceptionMessage[11]="Invalid state"),11); +var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = ((ExceptionMessage[12]="Syntax error"),12); +var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = ((ExceptionMessage[13]="Invalid modification"),13); +var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = ((ExceptionMessage[14]="Invalid namespace"),14); +var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = ((ExceptionMessage[15]="Invalid access"),15); + +/** + * DOM Level 2 + * Object DOMException + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html + * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html + */ +function DOMException(code, message) { + if(message instanceof Error){ + var error = message; + }else{ + error = this; + Error.call(this, ExceptionMessage[code]); + this.message = ExceptionMessage[code]; + if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException); + } + error.code = code; + if(message) this.message = this.message + ": " + message; + return error; +}; +DOMException.prototype = Error.prototype; +copy(ExceptionCode,DOMException) +/** + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177 + * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live. + * The items in the NodeList are accessible via an integral index, starting from 0. + */ +function NodeList() { +}; +NodeList.prototype = { + /** + * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive. + * @standard level1 + */ + length:0, + /** + * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null. + * @standard level1 + * @param index unsigned long + * Index into the collection. + * @return Node + * The node at the indexth position in the NodeList, or null if that is not a valid index. + */ + item: function(index) { + return this[index] || null; + }, + toString:function(isHTML,nodeFilter){ + for(var buf = [], i = 0;i=0){ + var lastIndex = list.length-1 + while(i0 || key == 'xmlns'){ +// return null; +// } + //console.log() + var i = this.length; + while(i--){ + var attr = this[i]; + //console.log(attr.nodeName,key) + if(attr.nodeName == key){ + return attr; + } + } + }, + setNamedItem: function(attr) { + var el = attr.ownerElement; + if(el && el!=this._ownerElement){ + throw new DOMException(INUSE_ATTRIBUTE_ERR); + } + var oldAttr = this.getNamedItem(attr.nodeName); + _addNamedNode(this._ownerElement,this,attr,oldAttr); + return oldAttr; + }, + /* returns Node */ + setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR + var el = attr.ownerElement, oldAttr; + if(el && el!=this._ownerElement){ + throw new DOMException(INUSE_ATTRIBUTE_ERR); + } + oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName); + _addNamedNode(this._ownerElement,this,attr,oldAttr); + return oldAttr; + }, + + /* returns Node */ + removeNamedItem: function(key) { + var attr = this.getNamedItem(key); + _removeNamedNode(this._ownerElement,this,attr); + return attr; + + + },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR + + //for level2 + removeNamedItemNS:function(namespaceURI,localName){ + var attr = this.getNamedItemNS(namespaceURI,localName); + _removeNamedNode(this._ownerElement,this,attr); + return attr; + }, + getNamedItemNS: function(namespaceURI, localName) { + var i = this.length; + while(i--){ + var node = this[i]; + if(node.localName == localName && node.namespaceURI == namespaceURI){ + return node; + } + } + return null; + } +}; +/** + * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 + */ +function DOMImplementation(/* Object */ features) { + this._features = {}; + if (features) { + for (var feature in features) { + this._features = features[feature]; + } + } +}; + +DOMImplementation.prototype = { + hasFeature: function(/* string */ feature, /* string */ version) { + var versions = this._features[feature.toLowerCase()]; + if (versions && (!version || version in versions)) { + return true; + } else { + return false; + } + }, + // Introduced in DOM Level 2: + createDocument:function(namespaceURI, qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR + var doc = new Document(); + doc.implementation = this; + doc.childNodes = new NodeList(); + doc.doctype = doctype; + if(doctype){ + doc.appendChild(doctype); + } + if(qualifiedName){ + var root = doc.createElementNS(namespaceURI,qualifiedName); + doc.appendChild(root); + } + return doc; + }, + // Introduced in DOM Level 2: + createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR + var node = new DocumentType(); + node.name = qualifiedName; + node.nodeName = qualifiedName; + node.publicId = publicId; + node.systemId = systemId; + // Introduced in DOM Level 2: + //readonly attribute DOMString internalSubset; + + //TODO:.. + // readonly attribute NamedNodeMap entities; + // readonly attribute NamedNodeMap notations; + return node; + } +}; + + +/** + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247 + */ + +function Node() { +}; + +Node.prototype = { + firstChild : null, + lastChild : null, + previousSibling : null, + nextSibling : null, + attributes : null, + parentNode : null, + childNodes : null, + ownerDocument : null, + nodeValue : null, + namespaceURI : null, + prefix : null, + localName : null, + // Modified in DOM Level 2: + insertBefore:function(newChild, refChild){//raises + return _insertBefore(this,newChild,refChild); + }, + replaceChild:function(newChild, oldChild){//raises + this.insertBefore(newChild,oldChild); + if(oldChild){ + this.removeChild(oldChild); + } + }, + removeChild:function(oldChild){ + return _removeChild(this,oldChild); + }, + appendChild:function(newChild){ + return this.insertBefore(newChild,null); + }, + hasChildNodes:function(){ + return this.firstChild != null; + }, + cloneNode:function(deep){ + return cloneNode(this.ownerDocument||this,this,deep); + }, + // Modified in DOM Level 2: + normalize:function(){ + var child = this.firstChild; + while(child){ + var next = child.nextSibling; + if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){ + this.removeChild(next); + child.appendData(next.data); + }else{ + child.normalize(); + child = next; + } + } + }, + // Introduced in DOM Level 2: + isSupported:function(feature, version){ + return this.ownerDocument.implementation.hasFeature(feature,version); + }, + // Introduced in DOM Level 2: + hasAttributes:function(){ + return this.attributes.length>0; + }, + lookupPrefix:function(namespaceURI){ + var el = this; + while(el){ + var map = el._nsMap; + //console.dir(map) + if(map){ + for(var n in map){ + if(map[n] == namespaceURI){ + return n; + } + } + } + el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; + } + return null; + }, + // Introduced in DOM Level 3: + lookupNamespaceURI:function(prefix){ + var el = this; + while(el){ + var map = el._nsMap; + //console.dir(map) + if(map){ + if(prefix in map){ + return map[prefix] ; + } + } + el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; + } + return null; + }, + // Introduced in DOM Level 3: + isDefaultNamespace:function(namespaceURI){ + var prefix = this.lookupPrefix(namespaceURI); + return prefix == null; + } +}; + + +function _xmlEncoder(c){ + return c == '<' && '<' || + c == '>' && '>' || + c == '&' && '&' || + c == '"' && '"' || + '&#'+c.charCodeAt()+';' +} + + +copy(NodeType,Node); +copy(NodeType,Node.prototype); + +/** + * @param callback return true for continue,false for break + * @return boolean true: break visit; + */ +function _visitNode(node,callback){ + if(callback(node)){ + return true; + } + if(node = node.firstChild){ + do{ + if(_visitNode(node,callback)){return true} + }while(node=node.nextSibling) + } +} + + + +function Document(){ +} +function _onAddAttribute(doc,el,newAttr){ + doc && doc._inc++; + var ns = newAttr.namespaceURI ; + if(ns == 'http://www.w3.org/2000/xmlns/'){ + //update namespace + el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value + } +} +function _onRemoveAttribute(doc,el,newAttr,remove){ + doc && doc._inc++; + var ns = newAttr.namespaceURI ; + if(ns == 'http://www.w3.org/2000/xmlns/'){ + //update namespace + delete el._nsMap[newAttr.prefix?newAttr.localName:''] + } +} +function _onUpdateChild(doc,el,newChild){ + if(doc && doc._inc){ + doc._inc++; + //update childNodes + var cs = el.childNodes; + if(newChild){ + cs[cs.length++] = newChild; + }else{ + //console.log(1) + var child = el.firstChild; + var i = 0; + while(child){ + cs[i++] = child; + child =child.nextSibling; + } + cs.length = i; + } + } +} + +/** + * attributes; + * children; + * + * writeable properties: + * nodeValue,Attr:value,CharacterData:data + * prefix + */ +function _removeChild(parentNode,child){ + var previous = child.previousSibling; + var next = child.nextSibling; + if(previous){ + previous.nextSibling = next; + }else{ + parentNode.firstChild = next + } + if(next){ + next.previousSibling = previous; + }else{ + parentNode.lastChild = previous; + } + _onUpdateChild(parentNode.ownerDocument,parentNode); + return child; +} +/** + * preformance key(refChild == null) + */ +function _insertBefore(parentNode,newChild,nextChild){ + var cp = newChild.parentNode; + if(cp){ + cp.removeChild(newChild);//remove and update + } + if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ + var newFirst = newChild.firstChild; + if (newFirst == null) { + return newChild; + } + var newLast = newChild.lastChild; + }else{ + newFirst = newLast = newChild; + } + var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild; + + newFirst.previousSibling = pre; + newLast.nextSibling = nextChild; + + + if(pre){ + pre.nextSibling = newFirst; + }else{ + parentNode.firstChild = newFirst; + } + if(nextChild == null){ + parentNode.lastChild = newLast; + }else{ + nextChild.previousSibling = newLast; + } + do{ + newFirst.parentNode = parentNode; + }while(newFirst !== newLast && (newFirst= newFirst.nextSibling)) + _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode); + //console.log(parentNode.lastChild.nextSibling == null) + if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) { + newChild.firstChild = newChild.lastChild = null; + } + return newChild; +} +function _appendSingleChild(parentNode,newChild){ + var cp = newChild.parentNode; + if(cp){ + var pre = parentNode.lastChild; + cp.removeChild(newChild);//remove and update + var pre = parentNode.lastChild; + } + var pre = parentNode.lastChild; + newChild.parentNode = parentNode; + newChild.previousSibling = pre; + newChild.nextSibling = null; + if(pre){ + pre.nextSibling = newChild; + }else{ + parentNode.firstChild = newChild; + } + parentNode.lastChild = newChild; + _onUpdateChild(parentNode.ownerDocument,parentNode,newChild); + return newChild; + //console.log("__aa",parentNode.lastChild.nextSibling == null) +} +Document.prototype = { + //implementation : null, + nodeName : '#document', + nodeType : DOCUMENT_NODE, + doctype : null, + documentElement : null, + _inc : 1, + + insertBefore : function(newChild, refChild){//raises + if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){ + var child = newChild.firstChild; + while(child){ + var next = child.nextSibling; + this.insertBefore(child,refChild); + child = next; + } + return newChild; + } + if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){ + this.documentElement = newChild; + } + + return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild; + }, + removeChild : function(oldChild){ + if(this.documentElement == oldChild){ + this.documentElement = null; + } + return _removeChild(this,oldChild); + }, + // Introduced in DOM Level 2: + importNode : function(importedNode,deep){ + return importNode(this,importedNode,deep); + }, + // Introduced in DOM Level 2: + getElementById : function(id){ + var rtv = null; + _visitNode(this.documentElement,function(node){ + if(node.nodeType == ELEMENT_NODE){ + if(node.getAttribute('id') == id){ + rtv = node; + return true; + } + } + }) + return rtv; + }, + + getElementsByClassName: function(className) { + var pattern = new RegExp("(^|\\s)" + className + "(\\s|$)"); + return new LiveNodeList(this, function(base) { + var ls = []; + _visitNode(base.documentElement, function(node) { + if(node !== base && node.nodeType == ELEMENT_NODE) { + if(pattern.test(node.getAttribute('class'))) { + ls.push(node); + } + } + }); + return ls; + }); + }, + + //document factory method: + createElement : function(tagName){ + var node = new Element(); + node.ownerDocument = this; + node.nodeName = tagName; + node.tagName = tagName; + node.childNodes = new NodeList(); + var attrs = node.attributes = new NamedNodeMap(); + attrs._ownerElement = node; + return node; + }, + createDocumentFragment : function(){ + var node = new DocumentFragment(); + node.ownerDocument = this; + node.childNodes = new NodeList(); + return node; + }, + createTextNode : function(data){ + var node = new Text(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createComment : function(data){ + var node = new Comment(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createCDATASection : function(data){ + var node = new CDATASection(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createProcessingInstruction : function(target,data){ + var node = new ProcessingInstruction(); + node.ownerDocument = this; + node.tagName = node.target = target; + node.nodeValue= node.data = data; + return node; + }, + createAttribute : function(name){ + var node = new Attr(); + node.ownerDocument = this; + node.name = name; + node.nodeName = name; + node.localName = name; + node.specified = true; + return node; + }, + createEntityReference : function(name){ + var node = new EntityReference(); + node.ownerDocument = this; + node.nodeName = name; + return node; + }, + // Introduced in DOM Level 2: + createElementNS : function(namespaceURI,qualifiedName){ + var node = new Element(); + var pl = qualifiedName.split(':'); + var attrs = node.attributes = new NamedNodeMap(); + node.childNodes = new NodeList(); + node.ownerDocument = this; + node.nodeName = qualifiedName; + node.tagName = qualifiedName; + node.namespaceURI = namespaceURI; + if(pl.length == 2){ + node.prefix = pl[0]; + node.localName = pl[1]; + }else{ + //el.prefix = null; + node.localName = qualifiedName; + } + attrs._ownerElement = node; + return node; + }, + // Introduced in DOM Level 2: + createAttributeNS : function(namespaceURI,qualifiedName){ + var node = new Attr(); + var pl = qualifiedName.split(':'); + node.ownerDocument = this; + node.nodeName = qualifiedName; + node.name = qualifiedName; + node.namespaceURI = namespaceURI; + node.specified = true; + if(pl.length == 2){ + node.prefix = pl[0]; + node.localName = pl[1]; + }else{ + //el.prefix = null; + node.localName = qualifiedName; + } + return node; + } +}; +_extends(Document,Node); + + +function Element() { + this._nsMap = {}; +}; +Element.prototype = { + nodeType : ELEMENT_NODE, + hasAttribute : function(name){ + return this.getAttributeNode(name)!=null; + }, + getAttribute : function(name){ + var attr = this.getAttributeNode(name); + return attr && attr.value || ''; + }, + getAttributeNode : function(name){ + return this.attributes.getNamedItem(name); + }, + setAttribute : function(name, value){ + var attr = this.ownerDocument.createAttribute(name); + attr.value = attr.nodeValue = "" + value; + this.setAttributeNode(attr) + }, + removeAttribute : function(name){ + var attr = this.getAttributeNode(name) + attr && this.removeAttributeNode(attr); + }, + + //four real opeartion method + appendChild:function(newChild){ + if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ + return this.insertBefore(newChild,null); + }else{ + return _appendSingleChild(this,newChild); + } + }, + setAttributeNode : function(newAttr){ + return this.attributes.setNamedItem(newAttr); + }, + setAttributeNodeNS : function(newAttr){ + return this.attributes.setNamedItemNS(newAttr); + }, + removeAttributeNode : function(oldAttr){ + //console.log(this == oldAttr.ownerElement) + return this.attributes.removeNamedItem(oldAttr.nodeName); + }, + //get real attribute name,and remove it by removeAttributeNode + removeAttributeNS : function(namespaceURI, localName){ + var old = this.getAttributeNodeNS(namespaceURI, localName); + old && this.removeAttributeNode(old); + }, + + hasAttributeNS : function(namespaceURI, localName){ + return this.getAttributeNodeNS(namespaceURI, localName)!=null; + }, + getAttributeNS : function(namespaceURI, localName){ + var attr = this.getAttributeNodeNS(namespaceURI, localName); + return attr && attr.value || ''; + }, + setAttributeNS : function(namespaceURI, qualifiedName, value){ + var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName); + attr.value = attr.nodeValue = "" + value; + this.setAttributeNode(attr) + }, + getAttributeNodeNS : function(namespaceURI, localName){ + return this.attributes.getNamedItemNS(namespaceURI, localName); + }, + + getElementsByTagName : function(tagName){ + return new LiveNodeList(this,function(base){ + var ls = []; + _visitNode(base,function(node){ + if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){ + ls.push(node); + } + }); + return ls; + }); + }, + getElementsByTagNameNS : function(namespaceURI, localName){ + return new LiveNodeList(this,function(base){ + var ls = []; + _visitNode(base,function(node){ + if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){ + ls.push(node); + } + }); + return ls; + + }); + } +}; +Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName; +Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS; + + +_extends(Element,Node); +function Attr() { +}; +Attr.prototype.nodeType = ATTRIBUTE_NODE; +_extends(Attr,Node); + + +function CharacterData() { +}; +CharacterData.prototype = { + data : '', + substringData : function(offset, count) { + return this.data.substring(offset, offset+count); + }, + appendData: function(text) { + text = this.data+text; + this.nodeValue = this.data = text; + this.length = text.length; + }, + insertData: function(offset,text) { + this.replaceData(offset,0,text); + + }, + appendChild:function(newChild){ + throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]) + }, + deleteData: function(offset, count) { + this.replaceData(offset,count,""); + }, + replaceData: function(offset, count, text) { + var start = this.data.substring(0,offset); + var end = this.data.substring(offset+count); + text = start + text + end; + this.nodeValue = this.data = text; + this.length = text.length; + } +} +_extends(CharacterData,Node); +function Text() { +}; +Text.prototype = { + nodeName : "#text", + nodeType : TEXT_NODE, + splitText : function(offset) { + var text = this.data; + var newText = text.substring(offset); + text = text.substring(0, offset); + this.data = this.nodeValue = text; + this.length = text.length; + var newNode = this.ownerDocument.createTextNode(newText); + if(this.parentNode){ + this.parentNode.insertBefore(newNode, this.nextSibling); + } + return newNode; + } +} +_extends(Text,CharacterData); +function Comment() { +}; +Comment.prototype = { + nodeName : "#comment", + nodeType : COMMENT_NODE +} +_extends(Comment,CharacterData); + +function CDATASection() { +}; +CDATASection.prototype = { + nodeName : "#cdata-section", + nodeType : CDATA_SECTION_NODE +} +_extends(CDATASection,CharacterData); + + +function DocumentType() { +}; +DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE; +_extends(DocumentType,Node); + +function Notation() { +}; +Notation.prototype.nodeType = NOTATION_NODE; +_extends(Notation,Node); + +function Entity() { +}; +Entity.prototype.nodeType = ENTITY_NODE; +_extends(Entity,Node); + +function EntityReference() { +}; +EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE; +_extends(EntityReference,Node); + +function DocumentFragment() { +}; +DocumentFragment.prototype.nodeName = "#document-fragment"; +DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE; +_extends(DocumentFragment,Node); + + +function ProcessingInstruction() { +} +ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE; +_extends(ProcessingInstruction,Node); +function XMLSerializer(){} +XMLSerializer.prototype.serializeToString = function(node,isHtml,nodeFilter){ + return nodeSerializeToString.call(node,isHtml,nodeFilter); +} +Node.prototype.toString = nodeSerializeToString; +function nodeSerializeToString(isHtml,nodeFilter){ + var buf = []; + var refNode = this.nodeType == 9 && this.documentElement || this; + var prefix = refNode.prefix; + var uri = refNode.namespaceURI; + + if(uri && prefix == null){ + //console.log(prefix) + var prefix = refNode.lookupPrefix(uri); + if(prefix == null){ + //isHTML = true; + var visibleNamespaces=[ + {namespace:uri,prefix:null} + //{namespace:uri,prefix:''} + ] + } + } + serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces); + //console.log('###',this.nodeType,uri,prefix,buf.join('')) + return buf.join(''); +} +function needNamespaceDefine(node,isHTML, visibleNamespaces) { + var prefix = node.prefix||''; + var uri = node.namespaceURI; + if (!prefix && !uri){ + return false; + } + if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" + || uri == 'http://www.w3.org/2000/xmlns/'){ + return false; + } + + var i = visibleNamespaces.length + //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces) + while (i--) { + var ns = visibleNamespaces[i]; + // get namespace prefix + //console.log(node.nodeType,node.tagName,ns.prefix,prefix) + if (ns.prefix == prefix){ + return ns.namespace != uri; + } + } + //console.log(isHTML,uri,prefix=='') + //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){ + // return false; + //} + //node.flag = '11111' + //console.error(3,true,node.flag,node.prefix,node.namespaceURI) + return true; +} +function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){ + if(nodeFilter){ + node = nodeFilter(node); + if(node){ + if(typeof node == 'string'){ + buf.push(node); + return; + } + }else{ + return; + } + //buf.sort.apply(attrs, attributeSorter); + } + switch(node.nodeType){ + case ELEMENT_NODE: + if (!visibleNamespaces) visibleNamespaces = []; + var startVisibleNamespaces = visibleNamespaces.length; + var attrs = node.attributes; + var len = attrs.length; + var child = node.firstChild; + var nodeName = node.tagName; + + isHTML = (htmlns === node.namespaceURI) ||isHTML + buf.push('<',nodeName); + + + + for(var i=0;i'); + //if is cdata child node + if(isHTML && /^script$/i.test(nodeName)){ + while(child){ + if(child.data){ + buf.push(child.data); + }else{ + serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); + } + child = child.nextSibling; + } + }else + { + while(child){ + serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); + child = child.nextSibling; + } + } + buf.push(''); + }else{ + buf.push('/>'); + } + // remove added visible namespaces + //visibleNamespaces.length = startVisibleNamespaces; + return; + case DOCUMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: + var child = node.firstChild; + while(child){ + serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); + child = child.nextSibling; + } + return; + case ATTRIBUTE_NODE: + /** + * Well-formedness constraint: No < in Attribute Values + * The replacement text of any entity referred to directly or indirectly in an attribute value must not contain a <. + * @see https://www.w3.org/TR/xml/#CleanAttrVals + * @see https://www.w3.org/TR/xml/#NT-AttValue + */ + return buf.push(' ', node.name, '="', node.value.replace(/[<&"]/g,_xmlEncoder), '"'); + case TEXT_NODE: + /** + * The ampersand character (&) and the left angle bracket (<) must not appear in their literal form, + * except when used as markup delimiters, or within a comment, a processing instruction, or a CDATA section. + * If they are needed elsewhere, they must be escaped using either numeric character references or the strings + * `&` and `<` respectively. + * The right angle bracket (>) may be represented using the string " > ", and must, for compatibility, + * be escaped using either `>` or a character reference when it appears in the string `]]>` in content, + * when that string is not marking the end of a CDATA section. + * + * In the content of elements, character data is any string of characters + * which does not contain the start-delimiter of any markup + * and does not include the CDATA-section-close delimiter, `]]>`. + * + * @see https://www.w3.org/TR/xml/#NT-CharData + */ + return buf.push(node.data + .replace(/[<&]/g,_xmlEncoder) + .replace(/]]>/g, ']]>') + ); + case CDATA_SECTION_NODE: + return buf.push( ''); + case COMMENT_NODE: + return buf.push( ""); + case DOCUMENT_TYPE_NODE: + var pubid = node.publicId; + var sysid = node.systemId; + buf.push(''); + }else if(sysid && sysid!='.'){ + buf.push(' SYSTEM ', sysid, '>'); + }else{ + var sub = node.internalSubset; + if(sub){ + buf.push(" [",sub,"]"); + } + buf.push(">"); + } + return; + case PROCESSING_INSTRUCTION_NODE: + return buf.push( ""); + case ENTITY_REFERENCE_NODE: + return buf.push( '&',node.nodeName,';'); + //case ENTITY_NODE: + //case NOTATION_NODE: + default: + buf.push('??',node.nodeName); + } +} +function importNode(doc,node,deep){ + var node2; + switch (node.nodeType) { + case ELEMENT_NODE: + node2 = node.cloneNode(false); + node2.ownerDocument = doc; + //var attrs = node2.attributes; + //var len = attrs.length; + //for(var i=0;i', + amp: '&', + quot: '"', + apos: "'", + Agrave: "À", + Aacute: "Á", + Acirc: "Â", + Atilde: "Ã", + Auml: "Ä", + Aring: "Å", + AElig: "Æ", + Ccedil: "Ç", + Egrave: "È", + Eacute: "É", + Ecirc: "Ê", + Euml: "Ë", + Igrave: "Ì", + Iacute: "Í", + Icirc: "Î", + Iuml: "Ï", + ETH: "Ð", + Ntilde: "Ñ", + Ograve: "Ò", + Oacute: "Ó", + Ocirc: "Ô", + Otilde: "Õ", + Ouml: "Ö", + Oslash: "Ø", + Ugrave: "Ù", + Uacute: "Ú", + Ucirc: "Û", + Uuml: "Ü", + Yacute: "Ý", + THORN: "Þ", + szlig: "ß", + agrave: "à", + aacute: "á", + acirc: "â", + atilde: "ã", + auml: "ä", + aring: "å", + aelig: "æ", + ccedil: "ç", + egrave: "è", + eacute: "é", + ecirc: "ê", + euml: "ë", + igrave: "ì", + iacute: "í", + icirc: "î", + iuml: "ï", + eth: "ð", + ntilde: "ñ", + ograve: "ò", + oacute: "ó", + ocirc: "ô", + otilde: "õ", + ouml: "ö", + oslash: "ø", + ugrave: "ù", + uacute: "ú", + ucirc: "û", + uuml: "ü", + yacute: "ý", + thorn: "þ", + yuml: "ÿ", + nbsp: "\u00a0", + iexcl: "¡", + cent: "¢", + pound: "£", + curren: "¤", + yen: "¥", + brvbar: "¦", + sect: "§", + uml: "¨", + copy: "©", + ordf: "ª", + laquo: "«", + not: "¬", + shy: "­­", + reg: "®", + macr: "¯", + deg: "°", + plusmn: "±", + sup2: "²", + sup3: "³", + acute: "´", + micro: "µ", + para: "¶", + middot: "·", + cedil: "¸", + sup1: "¹", + ordm: "º", + raquo: "»", + frac14: "¼", + frac12: "½", + frac34: "¾", + iquest: "¿", + times: "×", + divide: "÷", + forall: "∀", + part: "∂", + exist: "∃", + empty: "∅", + nabla: "∇", + isin: "∈", + notin: "∉", + ni: "∋", + prod: "∏", + sum: "∑", + minus: "−", + lowast: "∗", + radic: "√", + prop: "∝", + infin: "∞", + ang: "∠", + and: "∧", + or: "∨", + cap: "∩", + cup: "∪", + 'int': "∫", + there4: "∴", + sim: "∼", + cong: "≅", + asymp: "≈", + ne: "≠", + equiv: "≡", + le: "≤", + ge: "≥", + sub: "⊂", + sup: "⊃", + nsub: "⊄", + sube: "⊆", + supe: "⊇", + oplus: "⊕", + otimes: "⊗", + perp: "⊥", + sdot: "⋅", + Alpha: "Α", + Beta: "Β", + Gamma: "Γ", + Delta: "Δ", + Epsilon: "Ε", + Zeta: "Ζ", + Eta: "Η", + Theta: "Θ", + Iota: "Ι", + Kappa: "Κ", + Lambda: "Λ", + Mu: "Μ", + Nu: "Ν", + Xi: "Ξ", + Omicron: "Ο", + Pi: "Π", + Rho: "Ρ", + Sigma: "Σ", + Tau: "Τ", + Upsilon: "Υ", + Phi: "Φ", + Chi: "Χ", + Psi: "Ψ", + Omega: "Ω", + alpha: "α", + beta: "β", + gamma: "γ", + delta: "δ", + epsilon: "ε", + zeta: "ζ", + eta: "η", + theta: "θ", + iota: "ι", + kappa: "κ", + lambda: "λ", + mu: "μ", + nu: "ν", + xi: "ξ", + omicron: "ο", + pi: "π", + rho: "ρ", + sigmaf: "ς", + sigma: "σ", + tau: "τ", + upsilon: "υ", + phi: "φ", + chi: "χ", + psi: "ψ", + omega: "ω", + thetasym: "ϑ", + upsih: "ϒ", + piv: "ϖ", + OElig: "Œ", + oelig: "œ", + Scaron: "Š", + scaron: "š", + Yuml: "Ÿ", + fnof: "ƒ", + circ: "ˆ", + tilde: "˜", + ensp: " ", + emsp: " ", + thinsp: " ", + zwnj: "‌", + zwj: "‍", + lrm: "‎", + rlm: "‏", + ndash: "–", + mdash: "—", + lsquo: "‘", + rsquo: "’", + sbquo: "‚", + ldquo: "“", + rdquo: "”", + bdquo: "„", + dagger: "†", + Dagger: "‡", + bull: "•", + hellip: "…", + permil: "‰", + prime: "′", + Prime: "″", + lsaquo: "‹", + rsaquo: "›", + oline: "‾", + euro: "€", + trade: "™", + larr: "←", + uarr: "↑", + rarr: "→", + darr: "↓", + harr: "↔", + crarr: "↵", + lceil: "⌈", + rceil: "⌉", + lfloor: "⌊", + rfloor: "⌋", + loz: "◊", + spades: "♠", + clubs: "♣", + hearts: "♥", + diams: "♦" +}; + +},{}],7:[function(require,module,exports){ +//[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] +//[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] +//[5] Name ::= NameStartChar (NameChar)* +var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF +var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"); +var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$'); +//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ +//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') + +//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE +//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE +var S_TAG = 0;//tag name offerring +var S_ATTR = 1;//attr name offerring +var S_ATTR_SPACE=2;//attr name end and space offer +var S_EQ = 3;//=space? +var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only) +var S_ATTR_END = 5;//attr value end and no space(quot end) +var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer) +var S_TAG_CLOSE = 7;//closed el + +/** + * Creates an error that will not be caught by XMLReader aka the SAX parser. + * + * @param {string} message + * @param {any?} locator Optional, can provide details about the location in the source + * @constructor + */ +function ParseError(message, locator) { + this.message = message + this.locator = locator + if(Error.captureStackTrace) Error.captureStackTrace(this, ParseError); +} +ParseError.prototype = new Error(); +ParseError.prototype.name = ParseError.name + +function XMLReader(){ + +} + +XMLReader.prototype = { + parse:function(source,defaultNSMap,entityMap){ + var domBuilder = this.domBuilder; + domBuilder.startDocument(); + _copy(defaultNSMap ,defaultNSMap = {}) + parse(source,defaultNSMap,entityMap, + domBuilder,this.errorHandler); + domBuilder.endDocument(); + } +} +function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){ + function fixedFromCharCode(code) { + // String.prototype.fromCharCode does not supports + // > 2 bytes unicode chars directly + if (code > 0xffff) { + code -= 0x10000; + var surrogate1 = 0xd800 + (code >> 10) + , surrogate2 = 0xdc00 + (code & 0x3ff); + + return String.fromCharCode(surrogate1, surrogate2); + } else { + return String.fromCharCode(code); + } + } + function entityReplacer(a){ + var k = a.slice(1,-1); + if(k in entityMap){ + return entityMap[k]; + }else if(k.charAt(0) === '#'){ + return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x'))) + }else{ + errorHandler.error('entity not found:'+a); + return a; + } + } + function appendText(end){//has some bugs + if(end>start){ + var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer); + locator&&position(start); + domBuilder.characters(xt,0,end-start); + start = end + } + } + function position(p,m){ + while(p>=lineEnd && (m = linePattern.exec(source))){ + lineStart = m.index; + lineEnd = lineStart + m[0].length; + locator.lineNumber++; + //console.log('line++:',locator,startPos,endPos) + } + locator.columnNumber = p-lineStart+1; + } + var lineStart = 0; + var lineEnd = 0; + var linePattern = /.*(?:\r\n?|\n)|.*$/g + var locator = domBuilder.locator; + + var parseStack = [{currentNSMap:defaultNSMapCopy}] + var closeMap = {}; + var start = 0; + while(true){ + try{ + var tagStart = source.indexOf('<',start); + if(tagStart<0){ + if(!source.substr(start).match(/^\s*$/)){ + var doc = domBuilder.doc; + var text = doc.createTextNode(source.substr(start)); + doc.appendChild(text); + domBuilder.currentElement = text; + } + return; + } + if(tagStart>start){ + appendText(tagStart); + } + switch(source.charAt(tagStart+1)){ + case '/': + var end = source.indexOf('>',tagStart+3); + var tagName = source.substring(tagStart+2,end); + var config = parseStack.pop(); + if(end<0){ + + tagName = source.substring(tagStart+2).replace(/[\s<].*/,''); + errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName); + end = tagStart+1+tagName.length; + }else if(tagName.match(/\s + locator&&position(tagStart); + end = parseInstruction(source,tagStart,domBuilder); + break; + case '!':// start){ + start = end; + }else{ + //TODO: 这里有可能sax回退,有位置错误风险 + appendText(Math.max(tagStart,start)+1); + } + } +} +function copyLocator(f,t){ + t.lineNumber = f.lineNumber; + t.columnNumber = f.columnNumber; + return t; +} + +/** + * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack); + * @return end of the elementStartPart(end of elementEndPart for selfClosed el) + */ +function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){ + + /** + * @param {string} qname + * @param {string} value + * @param {number} startIndex + */ + function addAttribute(qname, value, startIndex) { + if (qname in el.attributeNames) errorHandler.fatalError('Attribute ' + qname + ' redefined') + el.addValue(qname, value, startIndex) + } + var attrName; + var value; + var p = ++start; + var s = S_TAG;//status + while(true){ + var c = source.charAt(p); + switch(c){ + case '=': + if(s === S_ATTR){//attrName + attrName = source.slice(start,p); + s = S_EQ; + }else if(s === S_ATTR_SPACE){ + s = S_EQ; + }else{ + //fatalError: equal must after attrName or space after attrName + throw new Error('attribute equal must after attrName'); // No known test case + } + break; + case '\'': + case '"': + if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE + ){//equal + if(s === S_ATTR){ + errorHandler.warning('attribute value must after "="') + attrName = source.slice(start,p) + } + start = p+1; + p = source.indexOf(c,start) + if(p>0){ + value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); + addAttribute(attrName, value, start-1); + s = S_ATTR_END; + }else{ + //fatalError: no end quot match + throw new Error('attribute value no end \''+c+'\' match'); + } + }else if(s == S_ATTR_NOQUOT_VALUE){ + value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); + //console.log(attrName,value,start,p) + addAttribute(attrName, value, start); + //console.dir(el) + errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!'); + start = p+1; + s = S_ATTR_END + }else{ + //fatalError: no equal before + throw new Error('attribute value must after "="'); // No known test case + } + break; + case '/': + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p)); + case S_ATTR_END: + case S_TAG_SPACE: + case S_TAG_CLOSE: + s =S_TAG_CLOSE; + el.closed = true; + case S_ATTR_NOQUOT_VALUE: + case S_ATTR: + case S_ATTR_SPACE: + break; + //case S_EQ: + default: + throw new Error("attribute invalid close char('/')") // No known test case + } + break; + case ''://end document + errorHandler.error('unexpected end of input'); + if(s == S_TAG){ + el.setTagName(source.slice(start,p)); + } + return p; + case '>': + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p)); + case S_ATTR_END: + case S_TAG_SPACE: + case S_TAG_CLOSE: + break;//normal + case S_ATTR_NOQUOT_VALUE://Compatible state + case S_ATTR: + value = source.slice(start,p); + if(value.slice(-1) === '/'){ + el.closed = true; + value = value.slice(0,-1) + } + case S_ATTR_SPACE: + if(s === S_ATTR_SPACE){ + value = attrName; + } + if(s == S_ATTR_NOQUOT_VALUE){ + errorHandler.warning('attribute "'+value+'" missed quot(")!'); + addAttribute(attrName, value.replace(/&#?\w+;/g,entityReplacer), start) + }else{ + if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){ + errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!') + } + addAttribute(value, value, start) + } + break; + case S_EQ: + throw new Error('attribute value missed!!'); + } +// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) + return p; + /*xml space '\x20' | #x9 | #xD | #xA; */ + case '\u0080': + c = ' '; + default: + if(c<= ' '){//space + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p));//tagName + s = S_TAG_SPACE; + break; + case S_ATTR: + attrName = source.slice(start,p) + s = S_ATTR_SPACE; + break; + case S_ATTR_NOQUOT_VALUE: + var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); + errorHandler.warning('attribute "'+value+'" missed quot(")!!'); + addAttribute(attrName, value, start) + case S_ATTR_END: + s = S_TAG_SPACE; + break; + //case S_TAG_SPACE: + //case S_EQ: + //case S_ATTR_SPACE: + // void();break; + //case S_TAG_CLOSE: + //ignore warning + } + }else{//not space +//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE +//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE + switch(s){ + //case S_TAG:void();break; + //case S_ATTR:void();break; + //case S_ATTR_NOQUOT_VALUE:void();break; + case S_ATTR_SPACE: + var tagName = el.tagName; + if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){ + errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!') + } + addAttribute(attrName, attrName, start); + start = p; + s = S_ATTR; + break; + case S_ATTR_END: + errorHandler.warning('attribute space is required"'+attrName+'"!!') + case S_TAG_SPACE: + s = S_ATTR; + start = p; + break; + case S_EQ: + s = S_ATTR_NOQUOT_VALUE; + start = p; + break; + case S_TAG_CLOSE: + throw new Error("elements closed character '/' and '>' must be connected to"); + } + } + }//end outer switch + //console.log('p++',p) + p++; + } +} +/** + * @return true if has new namespace define + */ +function appendElement(el,domBuilder,currentNSMap){ + var tagName = el.tagName; + var localNSMap = null; + //var currentNSMap = parseStack[parseStack.length-1].currentNSMap; + var i = el.length; + while(i--){ + var a = el[i]; + var qName = a.qName; + var value = a.value; + var nsp = qName.indexOf(':'); + if(nsp>0){ + var prefix = a.prefix = qName.slice(0,nsp); + var localName = qName.slice(nsp+1); + var nsPrefix = prefix === 'xmlns' && localName + }else{ + localName = qName; + prefix = null + nsPrefix = qName === 'xmlns' && '' + } + //can not set prefix,because prefix !== '' + a.localName = localName ; + //prefix == null for no ns prefix attribute + if(nsPrefix !== false){//hack!! + if(localNSMap == null){ + localNSMap = {} + //console.log(currentNSMap,0) + _copy(currentNSMap,currentNSMap={}) + //console.log(currentNSMap,1) + } + currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value; + a.uri = 'http://www.w3.org/2000/xmlns/' + domBuilder.startPrefixMapping(nsPrefix, value) + } + } + var i = el.length; + while(i--){ + a = el[i]; + var prefix = a.prefix; + if(prefix){//no prefix attribute has no namespace + if(prefix === 'xml'){ + a.uri = 'http://www.w3.org/XML/1998/namespace'; + }if(prefix !== 'xmlns'){ + a.uri = currentNSMap[prefix || ''] + + //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)} + } + } + } + var nsp = tagName.indexOf(':'); + if(nsp>0){ + prefix = el.prefix = tagName.slice(0,nsp); + localName = el.localName = tagName.slice(nsp+1); + }else{ + prefix = null;//important!! + localName = el.localName = tagName; + } + //no prefix element has default namespace + var ns = el.uri = currentNSMap[prefix || '']; + domBuilder.startElement(ns,localName,tagName,el); + //endPrefixMapping and startPrefixMapping have not any help for dom builder + //localNSMap = null + if(el.closed){ + domBuilder.endElement(ns,localName,tagName); + if(localNSMap){ + for(prefix in localNSMap){ + domBuilder.endPrefixMapping(prefix) + } + } + }else{ + el.currentNSMap = currentNSMap; + el.localNSMap = localNSMap; + //parseStack.push(el); + return true; + } +} +function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){ + if(/^(?:script|textarea)$/i.test(tagName)){ + var elEndStart = source.indexOf('',elStartEnd); + var text = source.substring(elStartEnd+1,elEndStart); + if(/[&<]/.test(text)){ + if(/^script$/i.test(tagName)){ + //if(!/\]\]>/.test(text)){ + //lexHandler.startCDATA(); + domBuilder.characters(text,0,text.length); + //lexHandler.endCDATA(); + return elEndStart; + //} + }//}else{//text area + text = text.replace(/&#?\w+;/g,entityReplacer); + domBuilder.characters(text,0,text.length); + return elEndStart; + //} + + } + } + return elStartEnd+1; +} +function fixSelfClosed(source,elStartEnd,tagName,closeMap){ + //if(tagName in closeMap){ + var pos = closeMap[tagName]; + if(pos == null){ + //console.log(tagName) + pos = source.lastIndexOf('') + if(pos',start+4); + //append comment source.substring(4,end)//' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + declaration(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + ''; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + docType(node, options, level) { + var child, i, len1, r, ref; + level || (level = 0); + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level); + r += ' 0) { + r += ' ['; + r += this.endline(node, options, level); + options.state = WriterState.InsideTag; + ref = node.children; + for (i = 0, len1 = ref.length; i < len1; i++) { + child = ref[i]; + r += this.writeChildNode(child, options, level + 1); + } + options.state = WriterState.CloseTag; + r += ']'; + } + // close tag + options.state = WriterState.CloseTag; + r += options.spaceBeforeSlash + '>'; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + element(node, options, level) { + var att, attLen, child, childNodeCount, firstChildNode, i, j, len, len1, len2, name, prettySuppressed, r, ratt, ref, ref1, ref2, ref3, rline; + level || (level = 0); + prettySuppressed = false; + // open tag + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '<' + node.name; + // attributes + if (options.pretty && options.width > 0) { + len = r.length; + ref = node.attribs; + for (name in ref) { + if (!hasProp.call(ref, name)) continue; + att = ref[name]; + ratt = this.attribute(att, options, level); + attLen = ratt.length; + if (len + attLen > options.width) { + rline = this.indent(node, options, level + 1) + ratt; + r += this.endline(node, options, level) + rline; + len = rline.length; + } else { + rline = ' ' + ratt; + r += rline; + len += rline.length; + } + } + } else { + ref1 = node.attribs; + for (name in ref1) { + if (!hasProp.call(ref1, name)) continue; + att = ref1[name]; + r += this.attribute(att, options, level); + } + } + childNodeCount = node.children.length; + firstChildNode = childNodeCount === 0 ? null : node.children[0]; + if (childNodeCount === 0 || node.children.every(function(e) { + return (e.type === NodeType.Text || e.type === NodeType.Raw || e.type === NodeType.CData) && e.value === ''; + })) { + // empty element + if (options.allowEmpty) { + r += '>'; + options.state = WriterState.CloseTag; + r += '' + this.endline(node, options, level); + } else { + options.state = WriterState.CloseTag; + r += options.spaceBeforeSlash + '/>' + this.endline(node, options, level); + } + } else if (options.pretty && childNodeCount === 1 && (firstChildNode.type === NodeType.Text || firstChildNode.type === NodeType.Raw || firstChildNode.type === NodeType.CData) && (firstChildNode.value != null)) { + // do not indent text-only nodes + r += '>'; + options.state = WriterState.InsideTag; + options.suppressPrettyCount++; + prettySuppressed = true; + r += this.writeChildNode(firstChildNode, options, level + 1); + options.suppressPrettyCount--; + prettySuppressed = false; + options.state = WriterState.CloseTag; + r += '' + this.endline(node, options, level); + } else { + // if ANY are a text node, then suppress pretty now + if (options.dontPrettyTextNodes) { + ref2 = node.children; + for (i = 0, len1 = ref2.length; i < len1; i++) { + child = ref2[i]; + if ((child.type === NodeType.Text || child.type === NodeType.Raw || child.type === NodeType.CData) && (child.value != null)) { + options.suppressPrettyCount++; + prettySuppressed = true; + break; + } + } + } + // close the opening tag, after dealing with newline + r += '>' + this.endline(node, options, level); + options.state = WriterState.InsideTag; + ref3 = node.children; + // inner tags + for (j = 0, len2 = ref3.length; j < len2; j++) { + child = ref3[j]; + r += this.writeChildNode(child, options, level + 1); + } + // close tag + options.state = WriterState.CloseTag; + r += this.indent(node, options, level) + ''; + if (prettySuppressed) { + options.suppressPrettyCount--; + } + r += this.endline(node, options, level); + options.state = WriterState.None; + } + this.closeNode(node, options, level); + return r; + } + + writeChildNode(node, options, level) { + switch (node.type) { + case NodeType.CData: + return this.cdata(node, options, level); + case NodeType.Comment: + return this.comment(node, options, level); + case NodeType.Element: + return this.element(node, options, level); + case NodeType.Raw: + return this.raw(node, options, level); + case NodeType.Text: + return this.text(node, options, level); + case NodeType.ProcessingInstruction: + return this.processingInstruction(node, options, level); + case NodeType.Dummy: + return ''; + case NodeType.Declaration: + return this.declaration(node, options, level); + case NodeType.DocType: + return this.docType(node, options, level); + case NodeType.AttributeDeclaration: + return this.dtdAttList(node, options, level); + case NodeType.ElementDeclaration: + return this.dtdElement(node, options, level); + case NodeType.EntityDeclaration: + return this.dtdEntity(node, options, level); + case NodeType.NotationDeclaration: + return this.dtdNotation(node, options, level); + default: + throw new Error("Unknown XML node type: " + node.constructor.name); + } + } + + processingInstruction(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + ''; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + raw(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level); + options.state = WriterState.InsideTag; + r += node.value; + options.state = WriterState.CloseTag; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + text(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level); + options.state = WriterState.InsideTag; + r += node.value; + options.state = WriterState.CloseTag; + r += this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + dtdAttList(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + dtdElement(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + dtdEntity(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + dtdNotation(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + } + + openNode(node, options, level) {} + + closeNode(node, options, level) {} + + openAttribute(att, options, level) {} + + closeAttribute(att, options, level) {} + + }; + +}).call(this); + +},{"./NodeType":13,"./Utility":14,"./WriterState":15,"./XMLCData":17,"./XMLComment":19,"./XMLDTDAttList":24,"./XMLDTDElement":25,"./XMLDTDEntity":26,"./XMLDTDNotation":27,"./XMLDeclaration":28,"./XMLDocType":29,"./XMLDummy":32,"./XMLElement":33,"./XMLProcessingInstruction":37,"./XMLRaw":38,"./XMLText":42}],44:[function(require,module,exports){ +// Generated by CoffeeScript 2.4.1 +(function() { + var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction; + + ({assign, isFunction} = require('./Utility')); + + XMLDOMImplementation = require('./XMLDOMImplementation'); + + XMLDocument = require('./XMLDocument'); + + XMLDocumentCB = require('./XMLDocumentCB'); + + XMLStringWriter = require('./XMLStringWriter'); + + XMLStreamWriter = require('./XMLStreamWriter'); + + NodeType = require('./NodeType'); + + WriterState = require('./WriterState'); + + // Creates a new document and returns the root node for + // chain-building the document tree + + // `name` name of the root element + + // `xmldec.version` A version number string, e.g. 1.0 + // `xmldec.encoding` Encoding declaration, e.g. UTF-8 + // `xmldec.standalone` standalone document declaration: true or false + + // `doctype.pubID` public identifier of the external subset + // `doctype.sysID` system identifier of the external subset + + // `options.headless` whether XML declaration and doctype will be included: + // true or false + // `options.keepNullNodes` whether nodes with null values will be kept + // or ignored: true or false + // `options.keepNullAttributes` whether attributes with null values will be + // kept or ignored: true or false + // `options.ignoreDecorators` whether decorator strings will be ignored when + // converting JS objects: true or false + // `options.separateArrayItems` whether array items are created as separate + // nodes when passed as an object value: true or false + // `options.noDoubleEncoding` whether existing html entities are encoded: + // true or false + // `options.stringify` a set of functions to use for converting values to + // strings + // `options.writer` the default XML writer to use for converting nodes to + // string. If the default writer is not set, the built-in XMLStringWriter + // will be used instead. + module.exports.create = function(name, xmldec, doctype, options) { + var doc, root; + if (name == null) { + throw new Error("Root element needs a name."); + } + options = assign({}, xmldec, doctype, options); + // create the document node + doc = new XMLDocument(options); + // add the root node + root = doc.element(name); + // prolog + if (!options.headless) { + doc.declaration(options); + if ((options.pubID != null) || (options.sysID != null)) { + doc.dtd(options); + } + } + return root; + }; + + // Creates a new document and returns the document node for + // chain-building the document tree + + // `options.keepNullNodes` whether nodes with null values will be kept + // or ignored: true or false + // `options.keepNullAttributes` whether attributes with null values will be + // kept or ignored: true or false + // `options.ignoreDecorators` whether decorator strings will be ignored when + // converting JS objects: true or false + // `options.separateArrayItems` whether array items are created as separate + // nodes when passed as an object value: true or false + // `options.noDoubleEncoding` whether existing html entities are encoded: + // true or false + // `options.stringify` a set of functions to use for converting values to + // strings + // `options.writer` the default XML writer to use for converting nodes to + // string. If the default writer is not set, the built-in XMLStringWriter + // will be used instead. + + // `onData` the function to be called when a new chunk of XML is output. The + // string containing the XML chunk is passed to `onData` as its single + // argument. + // `onEnd` the function to be called when the XML document is completed with + // `end`. `onEnd` does not receive any arguments. + module.exports.begin = function(options, onData, onEnd) { + if (isFunction(options)) { + [onData, onEnd] = [options, onData]; + options = {}; + } + if (onData) { + return new XMLDocumentCB(options, onData, onEnd); + } else { + return new XMLDocument(options); + } + }; + + module.exports.stringWriter = function(options) { + return new XMLStringWriter(options); + }; + + module.exports.streamWriter = function(stream, options) { + return new XMLStreamWriter(stream, options); + }; + + module.exports.implementation = new XMLDOMImplementation(); + + module.exports.nodeType = NodeType; + + module.exports.writerState = WriterState; + +}).call(this); + +},{"./NodeType":13,"./Utility":14,"./WriterState":15,"./XMLDOMImplementation":22,"./XMLDocument":30,"./XMLDocumentCB":31,"./XMLStreamWriter":39,"./XMLStringWriter":40}]},{},[1])(1) +}); diff --git a/node_modules/plist/examples/browser/index.html b/node_modules/plist/examples/browser/index.html new file mode 100644 index 0000000..8ce7d92 --- /dev/null +++ b/node_modules/plist/examples/browser/index.html @@ -0,0 +1,14 @@ + + + + plist.js browser example + + + + + + + diff --git a/node_modules/plist/index.js b/node_modules/plist/index.js new file mode 100644 index 0000000..d7385ae --- /dev/null +++ b/node_modules/plist/index.js @@ -0,0 +1,13 @@ +/** + * Parser functions. + */ + +var parserFunctions = require('./lib/parse'); +Object.keys(parserFunctions).forEach(function (k) { exports[k] = parserFunctions[k]; }); + +/** + * Builder functions. + */ + +var builderFunctions = require('./lib/build'); +Object.keys(builderFunctions).forEach(function (k) { exports[k] = builderFunctions[k]; }); diff --git a/node_modules/plist/lib/build.js b/node_modules/plist/lib/build.js new file mode 100644 index 0000000..c2a84b1 --- /dev/null +++ b/node_modules/plist/lib/build.js @@ -0,0 +1,143 @@ +/** + * Module dependencies. + */ + +var base64 = require('base64-js'); +var xmlbuilder = require('xmlbuilder'); + +/** + * Module exports. + */ + +exports.build = build; + +/** + * Accepts a `Date` instance and returns an ISO date string. + * + * @param {Date} d - Date instance to serialize + * @returns {String} ISO date string representation of `d` + * @api private + */ + +function ISODateString(d){ + function pad(n){ + return n < 10 ? '0' + n : n; + } + return d.getUTCFullYear()+'-' + + pad(d.getUTCMonth()+1)+'-' + + pad(d.getUTCDate())+'T' + + pad(d.getUTCHours())+':' + + pad(d.getUTCMinutes())+':' + + pad(d.getUTCSeconds())+'Z'; +} + +/** + * Returns the internal "type" of `obj` via the + * `Object.prototype.toString()` trick. + * + * @param {Mixed} obj - any value + * @returns {String} the internal "type" name + * @api private + */ + +var toString = Object.prototype.toString; +function type (obj) { + var m = toString.call(obj).match(/\[object (.*)\]/); + return m ? m[1] : m; +} + +/** + * Generate an XML plist string from the input object `obj`. + * + * @param {Object} obj - the object to convert + * @param {Object} [opts] - optional options object + * @returns {String} converted plist XML string + * @api public + */ + +function build (obj, opts) { + var XMLHDR = { + version: '1.0', + encoding: 'UTF-8' + }; + + var XMLDTD = { + pubid: '-//Apple//DTD PLIST 1.0//EN', + sysid: 'http://www.apple.com/DTDs/PropertyList-1.0.dtd' + }; + + var doc = xmlbuilder.create('plist'); + + doc.dec(XMLHDR.version, XMLHDR.encoding, XMLHDR.standalone); + doc.dtd(XMLDTD.pubid, XMLDTD.sysid); + doc.att('version', '1.0'); + + walk_obj(obj, doc); + + if (!opts) opts = {}; + // default `pretty` to `true` + opts.pretty = opts.pretty !== false; + return doc.end(opts); +} + +/** + * depth first, recursive traversal of a javascript object. when complete, + * next_child contains a reference to the build XML object. + * + * @api private + */ + +function walk_obj(next, next_child) { + var tag_type, i, prop; + var name = type(next); + + if ('Undefined' == name) { + return; + } else if (Array.isArray(next)) { + next_child = next_child.ele('array'); + for (i = 0; i < next.length; i++) { + walk_obj(next[i], next_child); + } + + } else if (Buffer.isBuffer(next)) { + next_child.ele('data').raw(next.toString('base64')); + + } else if ('Object' == name) { + next_child = next_child.ele('dict'); + for (prop in next) { + if (next.hasOwnProperty(prop)) { + next_child.ele('key').txt(prop); + walk_obj(next[prop], next_child); + } + } + + } else if ('Number' == name) { + // detect if this is an integer or real + // TODO: add an ability to force one way or another via a "cast" + tag_type = (next % 1 === 0) ? 'integer' : 'real'; + next_child.ele(tag_type).txt(next.toString()); + + } else if ('BigInt' == name) { + next_child.ele('integer').txt(next); + + } else if ('Date' == name) { + next_child.ele('date').txt(ISODateString(new Date(next))); + + } else if ('Boolean' == name) { + next_child.ele(next ? 'true' : 'false'); + + } else if ('String' == name) { + next_child.ele('string').txt(next); + + } else if ('ArrayBuffer' == name) { + next_child.ele('data').raw(base64.fromByteArray(next)); + + } else if (next && next.buffer && 'ArrayBuffer' == type(next.buffer)) { + // a typed array + next_child.ele('data').raw(base64.fromByteArray(new Uint8Array(next.buffer), next_child)); + + } else if ('Null' === name) { + next_child.ele('null').txt(''); + + } +} diff --git a/node_modules/plist/lib/parse.js b/node_modules/plist/lib/parse.js new file mode 100644 index 0000000..b5e7496 --- /dev/null +++ b/node_modules/plist/lib/parse.js @@ -0,0 +1,227 @@ +/** + * Module dependencies. + */ + +const { DOMParser } = require('@xmldom/xmldom'); + +/** + * Module exports. + */ + +exports.parse = parse; + +var TEXT_NODE = 3; +var CDATA_NODE = 4; +var COMMENT_NODE = 8; + + +/** + * We ignore raw text (usually whitespace), , + * and raw CDATA nodes. + * + * @param {Element} node + * @returns {Boolean} + * @api private + */ + +function shouldIgnoreNode (node) { + return node.nodeType === TEXT_NODE + || node.nodeType === COMMENT_NODE + || node.nodeType === CDATA_NODE; +} + +/** + * Check if the node is empty. Some plist file has such node: + * + * this node shoud be ignored. + * + * @see https://github.com/TooTallNate/plist.js/issues/66 + * @param {Element} node + * @returns {Boolean} + * @api private + */ +function isEmptyNode(node){ + if(!node.childNodes || node.childNodes.length === 0) { + return true; + } else { + return false; + } +} + +function invariant(test, message) { + if (!test) { + throw new Error(message); + } +} + +/** + * Parses a Plist XML string. Returns an Object. + * + * @param {String} xml - the XML String to decode + * @returns {Mixed} the decoded value from the Plist XML + * @api public + */ + +function parse (xml) { + var doc = new DOMParser().parseFromString(xml); + invariant( + doc.documentElement.nodeName === 'plist', + 'malformed document. First element should be ' + ); + var plist = parsePlistXML(doc.documentElement); + + // the root node gets interpreted as an Array, + // so pull out the inner data first + if (plist.length == 1) plist = plist[0]; + + return plist; +} + +/** + * Convert an XML based plist document into a JSON representation. + * + * @param {Object} xml_node - current XML node in the plist + * @returns {Mixed} built up JSON object + * @api private + */ + +function parsePlistXML (node) { + var i, new_obj, key, val, new_arr, res, counter, type; + + if (!node) + return null; + + if (node.nodeName === 'plist') { + new_arr = []; + if (isEmptyNode(node)) { + return new_arr; + } + for (i=0; i < node.childNodes.length; i++) { + if (!shouldIgnoreNode(node.childNodes[i])) { + new_arr.push( parsePlistXML(node.childNodes[i])); + } + } + return new_arr; + } else if (node.nodeName === 'dict') { + new_obj = {}; + key = null; + counter = 0; + if (isEmptyNode(node)) { + return new_obj; + } + for (i=0; i < node.childNodes.length; i++) { + if (shouldIgnoreNode(node.childNodes[i])) continue; + if (counter % 2 === 0) { + invariant( + node.childNodes[i].nodeName === 'key', + 'Missing key while parsing .' + ); + key = parsePlistXML(node.childNodes[i]); + } else { + invariant( + node.childNodes[i].nodeName !== 'key', + 'Unexpected key "' + + parsePlistXML(node.childNodes[i]) + + '" while parsing .' + ); + new_obj[key] = parsePlistXML(node.childNodes[i]); + } + counter += 1; + } + if (counter % 2 === 1) { + new_obj[key] = ''; + } + + return new_obj; + + } else if (node.nodeName === 'array') { + new_arr = []; + if (isEmptyNode(node)) { + return new_arr; + } + for (i=0; i < node.childNodes.length; i++) { + if (!shouldIgnoreNode(node.childNodes[i])) { + res = parsePlistXML(node.childNodes[i]); + if (null != res) new_arr.push(res); + } + } + return new_arr; + + } else if (node.nodeName === '#text') { + // TODO: what should we do with text types? (CDATA sections) + + } else if (node.nodeName === 'key') { + if (isEmptyNode(node)) { + return ''; + } + + invariant( + node.childNodes[0].nodeValue !== '__proto__', + '__proto__ keys can lead to prototype pollution. More details on CVE-2022-22912' + ); + + return node.childNodes[0].nodeValue; + } else if (node.nodeName === 'string') { + res = ''; + if (isEmptyNode(node)) { + return res; + } + for (i=0; i < node.childNodes.length; i++) { + var type = node.childNodes[i].nodeType; + if (type === TEXT_NODE || type === CDATA_NODE) { + res += node.childNodes[i].nodeValue; + } + } + return res; + + } else if (node.nodeName === 'integer') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as integer.' + ); + return parseInt(node.childNodes[0].nodeValue, 10); + + } else if (node.nodeName === 'real') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as real.' + ); + res = ''; + for (i=0; i < node.childNodes.length; i++) { + if (node.childNodes[i].nodeType === TEXT_NODE) { + res += node.childNodes[i].nodeValue; + } + } + return parseFloat(res); + + } else if (node.nodeName === 'data') { + res = ''; + if (isEmptyNode(node)) { + return Buffer.from(res, 'base64'); + } + for (i=0; i < node.childNodes.length; i++) { + if (node.childNodes[i].nodeType === TEXT_NODE) { + res += node.childNodes[i].nodeValue.replace(/\s+/g, ''); + } + } + return Buffer.from(res, 'base64'); + + } else if (node.nodeName === 'date') { + invariant( + !isEmptyNode(node), + 'Cannot parse "" as Date.' + ) + return new Date(node.childNodes[0].nodeValue); + + } else if (node.nodeName === 'null') { + return null; + + } else if (node.nodeName === 'true') { + return true; + + } else if (node.nodeName === 'false') { + return false; + } else { + throw new Error('Invalid PLIST tag ' + node.nodeName); + } +} diff --git a/node_modules/plist/package.json b/node_modules/plist/package.json new file mode 100644 index 0000000..ec86a62 --- /dev/null +++ b/node_modules/plist/package.json @@ -0,0 +1,44 @@ +{ + "name": "plist", + "description": "Apple's property list parser/builder for Node.js and browsers", + "version": "3.1.0", + "author": "Nathan Rajlich ", + "contributors": [ + "Hans Huebner ", + "Pierre Metrailler", + "Mike Reinstein ", + "Vladimir Tsvang", + "Mathieu D'Amours" + ], + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-plist.git" + }, + "license": "MIT", + "keywords": [ + "apple", + "browser", + "mac", + "plist", + "parser", + "xml" + ], + "main": "index.js", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "devDependencies": { + "browserify": "^17.0.0", + "mocha": "^9.2.2", + "multiline": "^2.0.0", + "zuul": "3.12.0" + }, + "scripts": { + "test": "make test" + }, + "engines": { + "node": ">=10.4.0" + } +} diff --git a/node_modules/q/CHANGES.md b/node_modules/q/CHANGES.md new file mode 100644 index 0000000..766fcdc --- /dev/null +++ b/node_modules/q/CHANGES.md @@ -0,0 +1,800 @@ + +## 1.5.1 + + - Q.any now annotates its error message to clarify that Q.any was involved and + includes only the last error emitted. (Ivan Etchart) + - Avoid domain.dispose during tests in preparation for Node.js 9. (Anna + Henningsen) + +## 1.5.0 + + - Q.any gives an error message from the last rejected promise + - Throw if callback supplied to "finally" is invalid (@grahamrhay) + - Long stack trace improvements, can now construct long stack traces + across rethrows. + +## 1.4.1 + + - Address an issue that prevented Q from being used as a ` + diff --git a/node_modules/sax/README.md b/node_modules/sax/README.md new file mode 100644 index 0000000..91a0314 --- /dev/null +++ b/node_modules/sax/README.md @@ -0,0 +1,220 @@ +# sax js + +A sax-style parser for XML and HTML. + +Designed with [node](http://nodejs.org/) in mind, but should work fine in +the browser or other CommonJS implementations. + +## What This Is + +* A very simple tool to parse through an XML string. +* A stepping stone to a streaming HTML parser. +* A handy way to deal with RSS and other mostly-ok-but-kinda-broken XML + docs. + +## What This Is (probably) Not + +* An HTML Parser - That's a fine goal, but this isn't it. It's just + XML. +* A DOM Builder - You can use it to build an object model out of XML, + but it doesn't do that out of the box. +* XSLT - No DOM = no querying. +* 100% Compliant with (some other SAX implementation) - Most SAX + implementations are in Java and do a lot more than this does. +* An XML Validator - It does a little validation when in strict mode, but + not much. +* A Schema-Aware XSD Thing - Schemas are an exercise in fetishistic + masochism. +* A DTD-aware Thing - Fetching DTDs is a much bigger job. + +## Regarding `Hello, world!').close(); + +// stream usage +// takes the same options as the parser +var saxStream = require("sax").createStream(strict, options) +saxStream.on("error", function (e) { + // unhandled errors will throw, since this is a proper node + // event emitter. + console.error("error!", e) + // clear the error + this._parser.error = null + this._parser.resume() +}) +saxStream.on("opentag", function (node) { + // same object as above +}) +// pipe is supported, and it's readable/writable +// same chunks coming in also go out. +fs.createReadStream("file.xml") + .pipe(saxStream) + .pipe(fs.createWriteStream("file-copy.xml")) +``` + + +## Arguments + +Pass the following arguments to the parser function. All are optional. + +`strict` - Boolean. Whether or not to be a jerk. Default: `false`. + +`opt` - Object bag of settings regarding string formatting. All default to `false`. + +Settings supported: + +* `trim` - Boolean. Whether or not to trim text and comment nodes. +* `normalize` - Boolean. If true, then turn any whitespace into a single + space. +* `lowercase` - Boolean. If true, then lowercase tag names and attribute names + in loose mode, rather than uppercasing them. +* `xmlns` - Boolean. If true, then namespaces are supported. +* `position` - Boolean. If false, then don't track line/col/position. +* `strictEntities` - Boolean. If true, only parse [predefined XML + entities](http://www.w3.org/TR/REC-xml/#sec-predefined-ent) + (`&`, `'`, `>`, `<`, and `"`) + +## Methods + +`write` - Write bytes onto the stream. You don't have to do this all at +once. You can keep writing as much as you want. + +`close` - Close the stream. Once closed, no more data may be written until +it is done processing the buffer, which is signaled by the `end` event. + +`resume` - To gracefully handle errors, assign a listener to the `error` +event. Then, when the error is taken care of, you can call `resume` to +continue parsing. Otherwise, the parser will not continue while in an error +state. + +## Members + +At all times, the parser object will have the following members: + +`line`, `column`, `position` - Indications of the position in the XML +document where the parser currently is looking. + +`startTagPosition` - Indicates the position where the current tag starts. + +`closed` - Boolean indicating whether or not the parser can be written to. +If it's `true`, then wait for the `ready` event to write again. + +`strict` - Boolean indicating whether or not the parser is a jerk. + +`opt` - Any options passed into the constructor. + +`tag` - The current tag being dealt with. + +And a bunch of other stuff that you probably shouldn't touch. + +## Events + +All events emit with a single argument. To listen to an event, assign a +function to `on`. Functions get executed in the this-context of +the parser object. The list of supported events are also in the exported +`EVENTS` array. + +When using the stream interface, assign handlers using the EventEmitter +`on` function in the normal fashion. + +`error` - Indication that something bad happened. The error will be hanging +out on `parser.error`, and must be deleted before parsing can continue. By +listening to this event, you can keep an eye on that kind of stuff. Note: +this happens *much* more in strict mode. Argument: instance of `Error`. + +`text` - Text node. Argument: string of text. + +`doctype` - The ``. Argument: +object with `name` and `body` members. Attributes are not parsed, as +processing instructions have implementation dependent semantics. + +`sgmldeclaration` - Random SGML declarations. Stuff like `` +would trigger this kind of event. This is a weird thing to support, so it +might go away at some point. SAX isn't intended to be used to parse SGML, +after all. + +`opentag` - An opening tag. Argument: object with `name` and `attributes`. +In non-strict mode, tag names are uppercased, unless the `lowercase` +option is set. If the `xmlns` option is set, then it will contain +namespace binding information on the `ns` member, and will have a +`local`, `prefix`, and `uri` member. + +`closetag` - A closing tag. In loose mode, tags are auto-closed if their +parent closes. In strict mode, well-formedness is enforced. Note that +self-closing tags will have `closeTag` emitted immediately after `openTag`. +Argument: tag name. + +`attribute` - An attribute node. Argument: object with `name` and `value`. +In non-strict mode, attribute names are uppercased, unless the `lowercase` +option is set. If the `xmlns` option is set, it will also contains namespace +information. + +`comment` - A comment node. Argument: the string of the comment. + +`opencdata` - The opening tag of a ``) of a `` tags trigger a `"script"` +event, and their contents are not checked for special xml characters. +If you pass `noscript: true`, then this behavior is suppressed. + +## Reporting Problems + +It's best to write a failing test if you find an issue. I will always +accept pull requests with failing tests if they demonstrate intended +behavior, but it is very hard to figure out what issue you're describing +without a test. Writing a test is also the best way for you yourself +to figure out if you really understand the issue you think you have with +sax-js. diff --git a/node_modules/sax/lib/sax.js b/node_modules/sax/lib/sax.js new file mode 100644 index 0000000..5c08d5f --- /dev/null +++ b/node_modules/sax/lib/sax.js @@ -0,0 +1,1563 @@ +;(function (sax) { // wrapper for non-node envs + sax.parser = function (strict, opt) { return new SAXParser(strict, opt) } + sax.SAXParser = SAXParser + sax.SAXStream = SAXStream + sax.createStream = createStream + + // When we pass the MAX_BUFFER_LENGTH position, start checking for buffer overruns. + // When we check, schedule the next check for MAX_BUFFER_LENGTH - (max(buffer lengths)), + // since that's the earliest that a buffer overrun could occur. This way, checks are + // as rare as required, but as often as necessary to ensure never crossing this bound. + // Furthermore, buffers are only tested at most once per write(), so passing a very + // large string into write() might have undesirable effects, but this is manageable by + // the caller, so it is assumed to be safe. Thus, a call to write() may, in the extreme + // edge case, result in creating at most one complete copy of the string passed in. + // Set to Infinity to have unlimited buffers. + sax.MAX_BUFFER_LENGTH = 64 * 1024 + + var buffers = [ + 'comment', 'sgmlDecl', 'textNode', 'tagName', 'doctype', + 'procInstName', 'procInstBody', 'entity', 'attribName', + 'attribValue', 'cdata', 'script' + ] + + sax.EVENTS = [ + 'text', + 'processinginstruction', + 'sgmldeclaration', + 'doctype', + 'comment', + 'attribute', + 'opentag', + 'closetag', + 'opencdata', + 'cdata', + 'closecdata', + 'error', + 'end', + 'ready', + 'script', + 'opennamespace', + 'closenamespace' + ] + + function SAXParser (strict, opt) { + if (!(this instanceof SAXParser)) { + return new SAXParser(strict, opt) + } + + var parser = this + clearBuffers(parser) + parser.q = parser.c = '' + parser.bufferCheckPosition = sax.MAX_BUFFER_LENGTH + parser.opt = opt || {} + parser.opt.lowercase = parser.opt.lowercase || parser.opt.lowercasetags + parser.looseCase = parser.opt.lowercase ? 'toLowerCase' : 'toUpperCase' + parser.tags = [] + parser.closed = parser.closedRoot = parser.sawRoot = false + parser.tag = parser.error = null + parser.strict = !!strict + parser.noscript = !!(strict || parser.opt.noscript) + parser.state = S.BEGIN + parser.strictEntities = parser.opt.strictEntities + parser.ENTITIES = parser.strictEntities ? Object.create(sax.XML_ENTITIES) : Object.create(sax.ENTITIES) + parser.attribList = [] + + // namespaces form a prototype chain. + // it always points at the current tag, + // which protos to its parent tag. + if (parser.opt.xmlns) { + parser.ns = Object.create(rootNS) + } + + // mostly just for error reporting + parser.trackPosition = parser.opt.position !== false + if (parser.trackPosition) { + parser.position = parser.line = parser.column = 0 + } + emit(parser, 'onready') + } + + if (!Object.create) { + Object.create = function (o) { + function F () {} + F.prototype = o + var newf = new F() + return newf + } + } + + if (!Object.keys) { + Object.keys = function (o) { + var a = [] + for (var i in o) if (o.hasOwnProperty(i)) a.push(i) + return a + } + } + + function checkBufferLength (parser) { + var maxAllowed = Math.max(sax.MAX_BUFFER_LENGTH, 10) + var maxActual = 0 + for (var i = 0, l = buffers.length; i < l; i++) { + var len = parser[buffers[i]].length + if (len > maxAllowed) { + // Text/cdata nodes can get big, and since they're buffered, + // we can get here under normal conditions. + // Avoid issues by emitting the text node now, + // so at least it won't get any bigger. + switch (buffers[i]) { + case 'textNode': + closeText(parser) + break + + case 'cdata': + emitNode(parser, 'oncdata', parser.cdata) + parser.cdata = '' + break + + case 'script': + emitNode(parser, 'onscript', parser.script) + parser.script = '' + break + + default: + error(parser, 'Max buffer length exceeded: ' + buffers[i]) + } + } + maxActual = Math.max(maxActual, len) + } + // schedule the next check for the earliest possible buffer overrun. + var m = sax.MAX_BUFFER_LENGTH - maxActual + parser.bufferCheckPosition = m + parser.position + } + + function clearBuffers (parser) { + for (var i = 0, l = buffers.length; i < l; i++) { + parser[buffers[i]] = '' + } + } + + function flushBuffers (parser) { + closeText(parser) + if (parser.cdata !== '') { + emitNode(parser, 'oncdata', parser.cdata) + parser.cdata = '' + } + if (parser.script !== '') { + emitNode(parser, 'onscript', parser.script) + parser.script = '' + } + } + + SAXParser.prototype = { + end: function () { end(this) }, + write: write, + resume: function () { this.error = null; return this }, + close: function () { return this.write(null) }, + flush: function () { flushBuffers(this) } + } + + var Stream + try { + Stream = require('stream').Stream + } catch (ex) { + Stream = function () {} + } + + var streamWraps = sax.EVENTS.filter(function (ev) { + return ev !== 'error' && ev !== 'end' + }) + + function createStream (strict, opt) { + return new SAXStream(strict, opt) + } + + function SAXStream (strict, opt) { + if (!(this instanceof SAXStream)) { + return new SAXStream(strict, opt) + } + + Stream.apply(this) + + this._parser = new SAXParser(strict, opt) + this.writable = true + this.readable = true + + var me = this + + this._parser.onend = function () { + me.emit('end') + } + + this._parser.onerror = function (er) { + me.emit('error', er) + + // if didn't throw, then means error was handled. + // go ahead and clear error, so we can write again. + me._parser.error = null + } + + this._decoder = null + + streamWraps.forEach(function (ev) { + Object.defineProperty(me, 'on' + ev, { + get: function () { + return me._parser['on' + ev] + }, + set: function (h) { + if (!h) { + me.removeAllListeners(ev) + me._parser['on' + ev] = h + return h + } + me.on(ev, h) + }, + enumerable: true, + configurable: false + }) + }) + } + + SAXStream.prototype = Object.create(Stream.prototype, { + constructor: { + value: SAXStream + } + }) + + SAXStream.prototype.write = function (data) { + if (typeof Buffer === 'function' && + typeof Buffer.isBuffer === 'function' && + Buffer.isBuffer(data)) { + if (!this._decoder) { + var SD = require('string_decoder').StringDecoder + this._decoder = new SD('utf8') + } + data = this._decoder.write(data) + } + + this._parser.write(data.toString()) + this.emit('data', data) + return true + } + + SAXStream.prototype.end = function (chunk) { + if (chunk && chunk.length) { + this.write(chunk) + } + this._parser.end() + return true + } + + SAXStream.prototype.on = function (ev, handler) { + var me = this + if (!me._parser['on' + ev] && streamWraps.indexOf(ev) !== -1) { + me._parser['on' + ev] = function () { + var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments) + args.splice(0, 0, ev) + me.emit.apply(me, args) + } + } + + return Stream.prototype.on.call(me, ev, handler) + } + + // character classes and tokens + var whitespace = '\r\n\t ' + + // this really needs to be replaced with character classes. + // XML allows all manner of ridiculous numbers and digits. + var number = '0124356789' + var letter = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + + // (Letter | "_" | ":") + var quote = '\'"' + var attribEnd = whitespace + '>' + var CDATA = '[CDATA[' + var DOCTYPE = 'DOCTYPE' + var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace' + var XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/' + var rootNS = { xml: XML_NAMESPACE, xmlns: XMLNS_NAMESPACE } + + // turn all the string character sets into character class objects. + whitespace = charClass(whitespace) + number = charClass(number) + letter = charClass(letter) + + // http://www.w3.org/TR/REC-xml/#NT-NameStartChar + // This implementation works on strings, a single character at a time + // as such, it cannot ever support astral-plane characters (10000-EFFFF) + // without a significant breaking change to either this parser, or the + // JavaScript language. Implementation of an emoji-capable xml parser + // is left as an exercise for the reader. + var nameStart = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/ + + var nameBody = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040\.\d-]/ + + var entityStart = /[#:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/ + var entityBody = /[#:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040\.\d-]/ + + quote = charClass(quote) + attribEnd = charClass(attribEnd) + + function charClass (str) { + return str.split('').reduce(function (s, c) { + s[c] = true + return s + }, {}) + } + + function isRegExp (c) { + return Object.prototype.toString.call(c) === '[object RegExp]' + } + + function is (charclass, c) { + return isRegExp(charclass) ? !!c.match(charclass) : charclass[c] + } + + function not (charclass, c) { + return !is(charclass, c) + } + + var S = 0 + sax.STATE = { + BEGIN: S++, // leading byte order mark or whitespace + BEGIN_WHITESPACE: S++, // leading whitespace + TEXT: S++, // general stuff + TEXT_ENTITY: S++, // & and such. + OPEN_WAKA: S++, // < + SGML_DECL: S++, // + SCRIPT: S++, // diff --git a/pages/index/customer.vue b/pages/index/customer.vue index 26a7b03..570ff69 100644 --- a/pages/index/customer.vue +++ b/pages/index/customer.vue @@ -25,7 +25,7 @@ {{item.created_at}} {{item.magnitude_txt}} - {{$t('followup')}} + {{item.follow_txt}} @@ -34,7 +34,7 @@ {{item.contacts}}{{item.phone}} {{item.grade_txt}} - {{item.sex_txt}} | 迪拜{{item.car_type_name}} + {{item.sex_txt}} | {{item.country_name}}{{item.car_type_name}} @@ -223,7 +223,9 @@ height: calc(100vh - 50px); } .navbar{ - padding-top: 44px; + /* #ifdef APP-PLUS */ + padding-top: 44px; + /* #endif */ height: 44px; line-height: 44px; text-align: center; @@ -255,7 +257,7 @@ padding: 6px 16px 2px; text-align: center; position: absolute; - top: 2px; + top: 3px; right: 5px; font-size: 12px; image{ diff --git a/pages/index/index.vue b/pages/index/index.vue index f925f6f..d409e35 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -1,17 +1,17 @@