1# this is based on jsarray.py
2
3import six
4try:
5    import numpy
6except:
7    pass
8
9if six.PY3:
10    xrange = range
11    import functools
12
13
14def to_arr(this):
15    """Returns Python array from Js array"""
16    return [this.get(str(e)) for e in xrange(len(this))]
17
18
19ARR_STACK = set({})
20
21
22class TypedArrayPrototype:
23    def toString():
24        # this function is wrong
25        func = this.get('join')
26        if not func.is_callable():
27
28            @this.Js
29            def func():
30                return '[object %s]' % this.Class
31
32        return func.call(this, ())
33
34    def toLocaleString(locales=None, options=None):
35        array = this.to_object()
36        arr_len = array.get("length").to_uint32()
37        # separator is simply a comma ','
38        if not arr_len:
39            return ''
40        res = []
41        for i in xrange(arr_len):
42            element = array[str(i)]
43            if element.is_undefined() or element.is_null():
44                res.append('')
45            else:
46                cand = element.to_object()
47                str_func = element.get('toLocaleString')
48                if not str_func.is_callable():
49                    raise this.MakeError(
50                        'TypeError',
51                        'toLocaleString method of item at index %d is not callable'
52                        % i)
53                res.append(element.callprop('toLocaleString').value)
54        return ','.join(res)
55
56    def join(separator):
57        ARR_STACK.add(this)
58        array = this.to_object()
59        arr_len = array.get("length").to_uint32()
60        separator = ',' if separator.is_undefined() else separator.to_string(
61        ).value
62        elems = []
63        for e in xrange(arr_len):
64            elem = array.get(str(e))
65            if elem in ARR_STACK:
66                s = ''
67            else:
68                s = elem.to_string().value
69            elems.append(
70                s if not (elem.is_undefined() or elem.is_null()) else '')
71        res = separator.join(elems)
72        ARR_STACK.remove(this)
73        return res
74
75    def reverse():
76        array = this.to_object()  # my own algorithm
77        vals = to_arr(array)
78        has_props = [array.has_property(str(e)) for e in xrange(len(array))]
79        vals.reverse()
80        has_props.reverse()
81        for i, val in enumerate(vals):
82            if has_props[i]:
83                array.put(str(i), val)
84            else:
85                array.delete(str(i))
86        return array
87
88    def slice(start, end):  # todo check
89        array = this.to_object()
90        arr_len = array.get("length").to_uint32()
91        relative_start = start.to_int()
92        k = max((arr_len + relative_start), 0) if relative_start < 0 else min(
93            relative_start, arr_len)
94        relative_end = arr_len if end.is_undefined() else end.to_int()
95        final = max((arr_len + relative_end), 0) if relative_end < 0 else min(
96            relative_end, arr_len)
97        res = []
98        n = 0
99        while k < final:
100            pk = str(k)
101            if array.has_property(pk):
102                res.append(array.get(pk))
103            k += 1
104            n += 1
105        return res
106
107    def sort(cmpfn):
108        if not this.Class in ('Array', 'Arguments'):
109            return this.to_object()  # do nothing
110        arr = []
111        for i in xrange(len(this)):
112            arr.append(this.get(six.text_type(i)))
113
114        if not arr:
115            return this
116        if not cmpfn.is_callable():
117            cmpfn = None
118        cmp = lambda a, b: sort_compare(a, b, cmpfn)
119        if six.PY3:
120            key = functools.cmp_to_key(cmp)
121            arr.sort(key=key)
122        else:
123            arr.sort(cmp=cmp)
124        for i in xrange(len(arr)):
125            this.put(six.text_type(i), arr[i])
126
127        return this
128
129    def indexOf(searchElement):
130        array = this.to_object()
131        arr_len = array.get("length").to_uint32()
132        if arr_len == 0:
133            return -1
134        if len(arguments) > 1:
135            n = arguments[1].to_int()
136        else:
137            n = 0
138        if n >= arr_len:
139            return -1
140        if n >= 0:
141            k = n
142        else:
143            k = arr_len - abs(n)
144            if k < 0:
145                k = 0
146        while k < arr_len:
147            if array.has_property(str(k)):
148                elementK = array.get(str(k))
149                if searchElement.strict_equality_comparison(elementK):
150                    return k
151            k += 1
152        return -1
153
154    def lastIndexOf(searchElement):
155        array = this.to_object()
156        arr_len = array.get("length").to_uint32()
157        if arr_len == 0:
158            return -1
159        if len(arguments) > 1:
160            n = arguments[1].to_int()
161        else:
162            n = arr_len - 1
163        if n >= 0:
164            k = min(n, arr_len - 1)
165        else:
166            k = arr_len - abs(n)
167        while k >= 0:
168            if array.has_property(str(k)):
169                elementK = array.get(str(k))
170                if searchElement.strict_equality_comparison(elementK):
171                    return k
172            k -= 1
173        return -1
174
175    def every(callbackfn):
176        array = this.to_object()
177        arr_len = array.get("length").to_uint32()
178        if not callbackfn.is_callable():
179            raise this.MakeError('TypeError', 'callbackfn must be a function')
180        T = arguments[1]
181        k = 0
182        while k < arr_len:
183            if array.has_property(str(k)):
184                kValue = array.get(str(k))
185                if not callbackfn.call(
186                        T, (kValue, this.Js(k), array)).to_boolean().value:
187                    return False
188            k += 1
189        return True
190
191    def some(callbackfn):
192        array = this.to_object()
193        arr_len = array.get("length").to_uint32()
194        if not callbackfn.is_callable():
195            raise this.MakeError('TypeError', 'callbackfn must be a function')
196        T = arguments[1]
197        k = 0
198        while k < arr_len:
199            if array.has_property(str(k)):
200                kValue = array.get(str(k))
201                if callbackfn.call(
202                        T, (kValue, this.Js(k), array)).to_boolean().value:
203                    return True
204            k += 1
205        return False
206
207    def forEach(callbackfn):
208        array = this.to_object()
209        arr_len = array.get("length").to_uint32()
210        if not callbackfn.is_callable():
211            raise this.MakeError('TypeError', 'callbackfn must be a function')
212        T = arguments[1]
213        k = 0
214        while k < arr_len:
215            if array.has_property(str(k)):
216                kValue = array.get(str(k))
217                callbackfn.call(T, (kValue, this.Js(k), array))
218            k += 1
219
220    def map(callbackfn):
221        array = this.to_object()
222        arr_len = array.get("length").to_uint32()
223        if not callbackfn.is_callable():
224            raise this.MakeError('TypeError', 'callbackfn must be a function')
225        T = arguments[1]
226        A = this.Js([])
227        k = 0
228        while k < arr_len:
229            Pk = str(k)
230            if array.has_property(Pk):
231                kValue = array.get(Pk)
232                mappedValue = callbackfn.call(T, (kValue, this.Js(k), array))
233                A.define_own_property(
234                    Pk, {
235                        'value': mappedValue,
236                        'writable': True,
237                        'enumerable': True,
238                        'configurable': True
239                    })
240            k += 1
241        return A
242
243    def filter(callbackfn):
244        array = this.to_object()
245        arr_len = array.get("length").to_uint32()
246        if not callbackfn.is_callable():
247            raise this.MakeError('TypeError', 'callbackfn must be a function')
248        T = arguments[1]
249        res = []
250        k = 0
251        while k < arr_len:
252            if array.has_property(str(k)):
253                kValue = array.get(str(k))
254                if callbackfn.call(
255                        T, (kValue, this.Js(k), array)).to_boolean().value:
256                    res.append(kValue)
257            k += 1
258        return res  # converted to js array automatically
259
260    def reduce(callbackfn):
261        array = this.to_object()
262        arr_len = array.get("length").to_uint32()
263        if not callbackfn.is_callable():
264            raise this.MakeError('TypeError', 'callbackfn must be a function')
265        if not arr_len and len(arguments) < 2:
266            raise this.MakeError(
267                'TypeError', 'Reduce of empty array with no initial value')
268        k = 0
269        if len(arguments) > 1:  # initial value present
270            accumulator = arguments[1]
271        else:
272            kPresent = False
273            while not kPresent and k < arr_len:
274                kPresent = array.has_property(str(k))
275                if kPresent:
276                    accumulator = array.get(str(k))
277                k += 1
278            if not kPresent:
279                raise this.MakeError(
280                    'TypeError', 'Reduce of empty array with no initial value')
281        while k < arr_len:
282            if array.has_property(str(k)):
283                kValue = array.get(str(k))
284                accumulator = callbackfn.call(
285                    this.undefined, (accumulator, kValue, this.Js(k), array))
286            k += 1
287        return accumulator
288
289    def reduceRight(callbackfn):
290        array = this.to_object()
291        arr_len = array.get("length").to_uint32()
292        if not callbackfn.is_callable():
293            raise this.MakeError('TypeError', 'callbackfn must be a function')
294        if not arr_len and len(arguments) < 2:
295            raise this.MakeError(
296                'TypeError', 'Reduce of empty array with no initial value')
297        k = arr_len - 1
298        if len(arguments) > 1:  # initial value present
299            accumulator = arguments[1]
300        else:
301            kPresent = False
302            while not kPresent and k >= 0:
303                kPresent = array.has_property(str(k))
304                if kPresent:
305                    accumulator = array.get(str(k))
306                k -= 1
307            if not kPresent:
308                raise this.MakeError(
309                    'TypeError', 'Reduce of empty array with no initial value')
310        while k >= 0:
311            if array.has_property(str(k)):
312                kValue = array.get(str(k))
313                accumulator = callbackfn.call(
314                    this.undefined, (accumulator, kValue, this.Js(k), array))
315            k -= 1
316        return accumulator
317
318
319def sort_compare(a, b, comp):
320    if a is None:
321        if b is None:
322            return 0
323        return 1
324    if b is None:
325        if a is None:
326            return 0
327        return -1
328    if a.is_undefined():
329        if b.is_undefined():
330            return 0
331        return 1
332    if b.is_undefined():
333        if a.is_undefined():
334            return 0
335        return -1
336    if comp is not None:
337        res = comp.call(a.undefined, (a, b))
338        return res.to_int()
339    x, y = a.to_string(), b.to_string()
340    if x < y:
341        return -1
342    elif x > y:
343        return 1
344    return 0
345