@@ -0,0 +1 @@
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
@@ -0,0 +1,476 @@
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
xrange = range
|
||||
import functools
|
||||
|
||||
|
||||
def to_arr(this):
|
||||
"""Returns Python array from Js array"""
|
||||
return [this.get(str(e)) for e in xrange(len(this))]
|
||||
|
||||
|
||||
ARR_STACK = set({})
|
||||
|
||||
|
||||
class ArrayPrototype:
|
||||
def toString():
|
||||
# this function is wrong but I will leave it here fore debugging purposes.
|
||||
func = this.get('join')
|
||||
if not func.is_callable():
|
||||
|
||||
@this.Js
|
||||
def func():
|
||||
return '[object %s]' % this.Class
|
||||
|
||||
return func.call(this, ())
|
||||
|
||||
def toLocaleString():
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
# separator is simply a comma ','
|
||||
if not arr_len:
|
||||
return ''
|
||||
res = []
|
||||
for i in xrange(arr_len):
|
||||
element = array[str(i)]
|
||||
if element.is_undefined() or element.is_null():
|
||||
res.append('')
|
||||
else:
|
||||
cand = element.to_object()
|
||||
str_func = element.get('toLocaleString')
|
||||
if not str_func.is_callable():
|
||||
raise this.MakeError(
|
||||
'TypeError',
|
||||
'toLocaleString method of item at index %d is not callable'
|
||||
% i)
|
||||
res.append(element.callprop('toLocaleString').value)
|
||||
return ','.join(res)
|
||||
|
||||
def concat():
|
||||
array = this.to_object()
|
||||
A = this.Js([])
|
||||
items = [array]
|
||||
items.extend(to_arr(arguments))
|
||||
n = 0
|
||||
for E in items:
|
||||
if E.Class == 'Array':
|
||||
k = 0
|
||||
e_len = len(E)
|
||||
while k < e_len:
|
||||
if E.has_property(str(k)):
|
||||
A.put(str(n), E.get(str(k)))
|
||||
n += 1
|
||||
k += 1
|
||||
else:
|
||||
A.put(str(n), E)
|
||||
n += 1
|
||||
return A
|
||||
|
||||
def join(separator):
|
||||
ARR_STACK.add(this)
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
separator = ',' if separator.is_undefined() else separator.to_string(
|
||||
).value
|
||||
elems = []
|
||||
for e in xrange(arr_len):
|
||||
elem = array.get(str(e))
|
||||
if elem in ARR_STACK:
|
||||
s = ''
|
||||
else:
|
||||
s = elem.to_string().value
|
||||
elems.append(
|
||||
s if not (elem.is_undefined() or elem.is_null()) else '')
|
||||
res = separator.join(elems)
|
||||
ARR_STACK.remove(this)
|
||||
return res
|
||||
|
||||
def pop(): #todo check
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not arr_len:
|
||||
array.put('length', this.Js(arr_len))
|
||||
return None
|
||||
ind = str(arr_len - 1)
|
||||
element = array.get(ind)
|
||||
array.delete(ind)
|
||||
array.put('length', this.Js(arr_len - 1))
|
||||
return element
|
||||
|
||||
def push(item): # todo check
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
to_put = arguments.to_list()
|
||||
i = arr_len
|
||||
for i, e in enumerate(to_put, arr_len):
|
||||
array.put(str(i), e)
|
||||
if to_put:
|
||||
i += 1
|
||||
array.put('length', this.Js(i))
|
||||
return i
|
||||
|
||||
def reverse():
|
||||
array = this.to_object() # my own algorithm
|
||||
vals = to_arr(array)
|
||||
has_props = [array.has_property(str(e)) for e in xrange(len(array))]
|
||||
vals.reverse()
|
||||
has_props.reverse()
|
||||
for i, val in enumerate(vals):
|
||||
if has_props[i]:
|
||||
array.put(str(i), val)
|
||||
else:
|
||||
array.delete(str(i))
|
||||
return array
|
||||
|
||||
def shift(): #todo check
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not arr_len:
|
||||
array.put('length', this.Js(0))
|
||||
return None
|
||||
first = array.get('0')
|
||||
for k in xrange(1, arr_len):
|
||||
from_s, to_s = str(k), str(k - 1)
|
||||
if array.has_property(from_s):
|
||||
array.put(to_s, array.get(from_s))
|
||||
else:
|
||||
array.delete(to)
|
||||
array.delete(str(arr_len - 1))
|
||||
array.put('length', this.Js(str(arr_len - 1)))
|
||||
return first
|
||||
|
||||
def slice(start, end): # todo check
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
relative_start = start.to_int()
|
||||
k = max((arr_len + relative_start), 0) if relative_start < 0 else min(
|
||||
relative_start, arr_len)
|
||||
relative_end = arr_len if end.is_undefined() else end.to_int()
|
||||
final = max((arr_len + relative_end), 0) if relative_end < 0 else min(
|
||||
relative_end, arr_len)
|
||||
res = []
|
||||
n = 0
|
||||
while k < final:
|
||||
pk = str(k)
|
||||
if array.has_property(pk):
|
||||
res.append(array.get(pk))
|
||||
k += 1
|
||||
n += 1
|
||||
return res
|
||||
|
||||
def sort(cmpfn):
|
||||
if not this.Class in ('Array', 'Arguments'):
|
||||
return this.to_object() # do nothing
|
||||
arr = []
|
||||
for i in xrange(len(this)):
|
||||
arr.append(this.get(six.text_type(i)))
|
||||
|
||||
if not arr:
|
||||
return this
|
||||
if not cmpfn.is_callable():
|
||||
cmpfn = None
|
||||
cmp = lambda a, b: sort_compare(a, b, cmpfn)
|
||||
if six.PY3:
|
||||
key = functools.cmp_to_key(cmp)
|
||||
arr.sort(key=key)
|
||||
else:
|
||||
arr.sort(cmp=cmp)
|
||||
for i in xrange(len(arr)):
|
||||
this.put(six.text_type(i), arr[i])
|
||||
|
||||
return this
|
||||
|
||||
def splice(start, deleteCount):
|
||||
# 1-8
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
relative_start = start.to_int()
|
||||
actual_start = max(
|
||||
(arr_len + relative_start), 0) if relative_start < 0 else min(
|
||||
relative_start, arr_len)
|
||||
actual_delete_count = min(
|
||||
max(deleteCount.to_int(), 0), arr_len - actual_start)
|
||||
k = 0
|
||||
A = this.Js([])
|
||||
# 9
|
||||
while k < actual_delete_count:
|
||||
if array.has_property(str(actual_start + k)):
|
||||
A.put(str(k), array.get(str(actual_start + k)))
|
||||
k += 1
|
||||
# 10-11
|
||||
items = to_arr(arguments)[2:]
|
||||
items_len = len(items)
|
||||
# 12
|
||||
if items_len < actual_delete_count:
|
||||
k = actual_start
|
||||
while k < (arr_len - actual_delete_count):
|
||||
fr = str(k + actual_delete_count)
|
||||
to = str(k + items_len)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k += 1
|
||||
k = arr_len
|
||||
while k > (arr_len - actual_delete_count + items_len):
|
||||
array.delete(str(k - 1))
|
||||
k -= 1
|
||||
# 13
|
||||
elif items_len > actual_delete_count:
|
||||
k = arr_len - actual_delete_count
|
||||
while k > actual_start:
|
||||
fr = str(k + actual_delete_count - 1)
|
||||
to = str(k + items_len - 1)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k -= 1
|
||||
# 14-17
|
||||
k = actual_start
|
||||
while items:
|
||||
E = items.pop(0)
|
||||
array.put(str(k), E)
|
||||
k += 1
|
||||
array.put('length', this.Js(arr_len - actual_delete_count + items_len))
|
||||
return A
|
||||
|
||||
def unshift():
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
argCount = len(arguments)
|
||||
k = arr_len
|
||||
while k > 0:
|
||||
fr = str(k - 1)
|
||||
to = str(k + argCount - 1)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k -= 1
|
||||
j = 0
|
||||
items = to_arr(arguments)
|
||||
while items:
|
||||
E = items.pop(0)
|
||||
array.put(str(j), E)
|
||||
j += 1
|
||||
array.put('length', this.Js(arr_len + argCount))
|
||||
return arr_len + argCount
|
||||
|
||||
def indexOf(searchElement):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if arr_len == 0:
|
||||
return -1
|
||||
if len(arguments) > 1:
|
||||
n = arguments[1].to_int()
|
||||
else:
|
||||
n = 0
|
||||
if n >= arr_len:
|
||||
return -1
|
||||
if n >= 0:
|
||||
k = n
|
||||
else:
|
||||
k = arr_len - abs(n)
|
||||
if k < 0:
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
elementK = array.get(str(k))
|
||||
if searchElement.strict_equality_comparison(elementK):
|
||||
return k
|
||||
k += 1
|
||||
return -1
|
||||
|
||||
def lastIndexOf(searchElement):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if arr_len == 0:
|
||||
return -1
|
||||
if len(arguments) > 1:
|
||||
n = arguments[1].to_int()
|
||||
else:
|
||||
n = arr_len - 1
|
||||
if n >= 0:
|
||||
k = min(n, arr_len - 1)
|
||||
else:
|
||||
k = arr_len - abs(n)
|
||||
while k >= 0:
|
||||
if array.has_property(str(k)):
|
||||
elementK = array.get(str(k))
|
||||
if searchElement.strict_equality_comparison(elementK):
|
||||
return k
|
||||
k -= 1
|
||||
return -1
|
||||
|
||||
def every(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
if not callbackfn.call(
|
||||
T, (kValue, this.Js(k), array)).to_boolean().value:
|
||||
return False
|
||||
k += 1
|
||||
return True
|
||||
|
||||
def some(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
if callbackfn.call(
|
||||
T, (kValue, this.Js(k), array)).to_boolean().value:
|
||||
return True
|
||||
k += 1
|
||||
return False
|
||||
|
||||
def forEach(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
callbackfn.call(T, (kValue, this.Js(k), array))
|
||||
k += 1
|
||||
|
||||
def map(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
A = this.Js([])
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
Pk = str(k)
|
||||
if array.has_property(Pk):
|
||||
kValue = array.get(Pk)
|
||||
mappedValue = callbackfn.call(T, (kValue, this.Js(k), array))
|
||||
A.define_own_property(
|
||||
Pk, {
|
||||
'value': mappedValue,
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
})
|
||||
k += 1
|
||||
return A
|
||||
|
||||
def filter(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
res = []
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
if callbackfn.call(
|
||||
T, (kValue, this.Js(k), array)).to_boolean().value:
|
||||
res.append(kValue)
|
||||
k += 1
|
||||
return res # converted to js array automatically
|
||||
|
||||
def reduce(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
if not arr_len and len(arguments) < 2:
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Reduce of empty array with no initial value')
|
||||
k = 0
|
||||
if len(arguments) > 1: # initial value present
|
||||
accumulator = arguments[1]
|
||||
else:
|
||||
kPresent = False
|
||||
while not kPresent and k < arr_len:
|
||||
kPresent = array.has_property(str(k))
|
||||
if kPresent:
|
||||
accumulator = array.get(str(k))
|
||||
k += 1
|
||||
if not kPresent:
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Reduce of empty array with no initial value')
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
accumulator = callbackfn.call(
|
||||
this.undefined, (accumulator, kValue, this.Js(k), array))
|
||||
k += 1
|
||||
return accumulator
|
||||
|
||||
def reduceRight(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get('length').to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
if not arr_len and len(arguments) < 2:
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Reduce of empty array with no initial value')
|
||||
k = arr_len - 1
|
||||
if len(arguments) > 1: # initial value present
|
||||
accumulator = arguments[1]
|
||||
else:
|
||||
kPresent = False
|
||||
while not kPresent and k >= 0:
|
||||
kPresent = array.has_property(str(k))
|
||||
if kPresent:
|
||||
accumulator = array.get(str(k))
|
||||
k -= 1
|
||||
if not kPresent:
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Reduce of empty array with no initial value')
|
||||
while k >= 0:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
accumulator = callbackfn.call(
|
||||
this.undefined, (accumulator, kValue, this.Js(k), array))
|
||||
k -= 1
|
||||
return accumulator
|
||||
|
||||
|
||||
def sort_compare(a, b, comp):
|
||||
if a is None:
|
||||
if b is None:
|
||||
return 0
|
||||
return 1
|
||||
if b is None:
|
||||
if a is None:
|
||||
return 0
|
||||
return -1
|
||||
if a.is_undefined():
|
||||
if b.is_undefined():
|
||||
return 0
|
||||
return 1
|
||||
if b.is_undefined():
|
||||
if a.is_undefined():
|
||||
return 0
|
||||
return -1
|
||||
if comp is not None:
|
||||
res = comp.call(a.undefined, (a, b))
|
||||
return res.to_int()
|
||||
x, y = a.to_string(), b.to_string()
|
||||
if x < y:
|
||||
return -1
|
||||
elif x > y:
|
||||
return 1
|
||||
return 0
|
||||
@@ -0,0 +1,19 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
xrange = range
|
||||
import functools
|
||||
|
||||
|
||||
def to_arr(this):
|
||||
"""Returns Python array from Js array"""
|
||||
return [this.get(str(e)) for e in xrange(len(this))]
|
||||
|
||||
|
||||
ARR_STACK = set({})
|
||||
|
||||
|
||||
class ArrayBufferPrototype:
|
||||
pass
|
||||
@@ -0,0 +1,10 @@
|
||||
class BooleanPrototype:
|
||||
def toString():
|
||||
if this.Class != 'Boolean':
|
||||
raise this.Js(TypeError)('this must be a boolean')
|
||||
return 'true' if this.value else 'false'
|
||||
|
||||
def valueOf():
|
||||
if this.Class != 'Boolean':
|
||||
raise this.Js(TypeError)('this must be a boolean')
|
||||
return this.value
|
||||
@@ -0,0 +1,10 @@
|
||||
class ErrorPrototype:
|
||||
def toString():
|
||||
if this.TYPE != 'Object':
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Error.prototype.toString called on non-object')
|
||||
name = this.get('name')
|
||||
name = 'Error' if name.is_undefined() else name.to_string().value
|
||||
msg = this.get('message')
|
||||
msg = '' if msg.is_undefined() else msg.to_string().value
|
||||
return name + (name and msg and ': ') + msg
|
||||
@@ -0,0 +1,52 @@
|
||||
# python 3 support
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
# todo fix apply and bind
|
||||
|
||||
|
||||
class FunctionPrototype:
|
||||
def toString():
|
||||
if not this.is_callable():
|
||||
raise TypeError('toString is not generic!')
|
||||
args = ', '.join(this.code.__code__.co_varnames[:this.argcount])
|
||||
return 'function %s(%s) ' % (this.func_name, args) + this.source
|
||||
|
||||
def call():
|
||||
arguments_ = arguments
|
||||
if not len(arguments):
|
||||
obj = this.Js(None)
|
||||
else:
|
||||
obj = arguments[0]
|
||||
if len(arguments) <= 1:
|
||||
args = ()
|
||||
else:
|
||||
args = tuple([arguments_[e] for e in xrange(1, len(arguments_))])
|
||||
return this.call(obj, args)
|
||||
|
||||
def apply():
|
||||
if not len(arguments):
|
||||
obj = this.Js(None)
|
||||
else:
|
||||
obj = arguments[0]
|
||||
if len(arguments) <= 1:
|
||||
args = ()
|
||||
else:
|
||||
appl = arguments[1]
|
||||
args = tuple([appl[e] for e in xrange(len(appl))])
|
||||
return this.call(obj, args)
|
||||
|
||||
def bind(thisArg):
|
||||
target = this
|
||||
if not target.is_callable():
|
||||
raise this.MakeError(
|
||||
'Object must be callable in order to be used with bind method')
|
||||
if len(arguments) <= 1:
|
||||
args = ()
|
||||
else:
|
||||
args = tuple([arguments[e] for e in xrange(1, len(arguments))])
|
||||
return this.PyJsBoundFunction(target, thisArg, args)
|
||||
@@ -0,0 +1,219 @@
|
||||
import json
|
||||
from ..base import Js
|
||||
indent = ''
|
||||
# python 3 support
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
|
||||
def parse(text):
|
||||
reviver = arguments[1]
|
||||
s = text.to_string().value
|
||||
try:
|
||||
unfiltered = json.loads(s)
|
||||
except:
|
||||
raise this.MakeError('SyntaxError',
|
||||
'Could not parse JSON string - Invalid syntax')
|
||||
unfiltered = to_js(this, unfiltered)
|
||||
if reviver.is_callable():
|
||||
root = this.Js({'': unfiltered})
|
||||
return walk(root, '', reviver)
|
||||
else:
|
||||
return unfiltered
|
||||
|
||||
|
||||
def stringify(value, replacer, space):
|
||||
global indent
|
||||
stack = set([])
|
||||
indent = ''
|
||||
property_list = replacer_function = this.undefined
|
||||
if replacer.is_object():
|
||||
if replacer.is_callable():
|
||||
replacer_function = replacer
|
||||
elif replacer.Class == 'Array':
|
||||
property_list = []
|
||||
for e in replacer:
|
||||
v = replacer[e]
|
||||
item = this.undefined
|
||||
if v._type() == 'Number':
|
||||
item = v.to_string()
|
||||
elif v._type() == 'String':
|
||||
item = v
|
||||
elif v.is_object():
|
||||
if v.Class in ('String', 'Number'):
|
||||
item = v.to_string()
|
||||
if not item.is_undefined() and item.value not in property_list:
|
||||
property_list.append(item.value)
|
||||
if space.is_object():
|
||||
if space.Class == 'Number':
|
||||
space = space.to_number()
|
||||
elif space.Class == 'String':
|
||||
space = space.to_string()
|
||||
if space._type() == 'Number':
|
||||
space = this.Js(min(10, space.to_int()))
|
||||
gap = max(int(space.value), 0) * ' '
|
||||
elif space._type() == 'String':
|
||||
gap = space.value[:10]
|
||||
else:
|
||||
gap = ''
|
||||
return this.Js(
|
||||
Str('', this.Js({
|
||||
'': value
|
||||
}), replacer_function, property_list, gap, stack, space))
|
||||
|
||||
|
||||
def Str(key, holder, replacer_function, property_list, gap, stack, space):
|
||||
value = holder[key]
|
||||
if value.is_object():
|
||||
to_json = value.get('toJSON')
|
||||
if to_json.is_callable():
|
||||
value = to_json.call(value, (key, ))
|
||||
if not replacer_function.is_undefined():
|
||||
value = replacer_function.call(holder, (key, value))
|
||||
|
||||
if value.is_object():
|
||||
if value.Class == 'String':
|
||||
value = value.to_string()
|
||||
elif value.Class == 'Number':
|
||||
value = value.to_number()
|
||||
elif value.Class == 'Boolean':
|
||||
value = value.to_boolean()
|
||||
if value.is_null():
|
||||
return 'null'
|
||||
elif value.Class == 'Boolean':
|
||||
return 'true' if value.value else 'false'
|
||||
elif value._type() == 'String':
|
||||
return Quote(value)
|
||||
elif value._type() == 'Number':
|
||||
if not value.is_infinity():
|
||||
return value.to_string()
|
||||
return 'null'
|
||||
if value.is_object() and not value.is_callable():
|
||||
if value.Class == 'Array':
|
||||
return ja(value, stack, gap, property_list, replacer_function,
|
||||
space)
|
||||
else:
|
||||
return jo(value, stack, gap, property_list, replacer_function,
|
||||
space)
|
||||
return None # undefined
|
||||
|
||||
|
||||
def jo(value, stack, gap, property_list, replacer_function, space):
|
||||
global indent
|
||||
if value in stack:
|
||||
raise value.MakeError('TypeError',
|
||||
'Converting circular structure to JSON')
|
||||
stack.add(value)
|
||||
stepback = indent
|
||||
indent += gap
|
||||
if not property_list.is_undefined():
|
||||
k = property_list
|
||||
else:
|
||||
k = [e.value for e in value]
|
||||
partial = []
|
||||
for p in k:
|
||||
str_p = value.Js(
|
||||
Str(p, value, replacer_function, property_list, gap, stack, space))
|
||||
if not str_p.is_undefined():
|
||||
member = json.dumps(p) + ':' + (
|
||||
' ' if gap else
|
||||
'') + str_p.value # todo not sure here - what space character?
|
||||
partial.append(member)
|
||||
if not partial:
|
||||
final = '{}'
|
||||
else:
|
||||
if not gap:
|
||||
final = '{%s}' % ','.join(partial)
|
||||
else:
|
||||
sep = ',\n' + indent
|
||||
properties = sep.join(partial)
|
||||
final = '{\n' + indent + properties + '\n' + stepback + '}'
|
||||
stack.remove(value)
|
||||
indent = stepback
|
||||
return final
|
||||
|
||||
|
||||
def ja(value, stack, gap, property_list, replacer_function, space):
|
||||
global indent
|
||||
if value in stack:
|
||||
raise value.MakeError('TypeError',
|
||||
'Converting circular structure to JSON')
|
||||
stack.add(value)
|
||||
stepback = indent
|
||||
indent += gap
|
||||
partial = []
|
||||
length = len(value)
|
||||
for index in xrange(length):
|
||||
index = str(index)
|
||||
str_index = value.Js(
|
||||
Str(index, value, replacer_function, property_list, gap, stack,
|
||||
space))
|
||||
if str_index.is_undefined():
|
||||
partial.append('null')
|
||||
else:
|
||||
partial.append(str_index.value)
|
||||
if not partial:
|
||||
final = '[]'
|
||||
else:
|
||||
if not gap:
|
||||
final = '[%s]' % ','.join(partial)
|
||||
else:
|
||||
sep = ',\n' + indent
|
||||
properties = sep.join(partial)
|
||||
final = '[\n' + indent + properties + '\n' + stepback + ']'
|
||||
stack.remove(value)
|
||||
indent = stepback
|
||||
return final
|
||||
|
||||
|
||||
def Quote(string):
|
||||
return string.Js(json.dumps(string.value))
|
||||
|
||||
|
||||
def to_js(this, d):
|
||||
if isinstance(d, dict):
|
||||
return this.Js(dict((k, this.Js(v)) for k, v in six.iteritems(d)))
|
||||
return this.Js(d)
|
||||
|
||||
|
||||
def walk(holder, name, reviver):
|
||||
val = holder.get(name)
|
||||
if val.Class == 'Array':
|
||||
for i in xrange(len(val)):
|
||||
i = unicode(i)
|
||||
new_element = walk(val, i, reviver)
|
||||
if new_element.is_undefined():
|
||||
val.delete(i)
|
||||
else:
|
||||
new_element.put(i, new_element)
|
||||
elif val.is_object():
|
||||
for key in val:
|
||||
new_element = walk(val, key, reviver)
|
||||
if new_element.is_undefined():
|
||||
val.delete(key)
|
||||
else:
|
||||
val.put(key, new_element)
|
||||
return reviver.call(holder, (name, val))
|
||||
|
||||
|
||||
JSON = Js({})
|
||||
|
||||
JSON.define_own_property(
|
||||
'parse', {
|
||||
'value': Js(parse),
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
JSON.define_own_property(
|
||||
'stringify', {
|
||||
'value': Js(stringify),
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -0,0 +1,146 @@
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
RADIX_SYMBOLS = {
|
||||
0: '0',
|
||||
1: '1',
|
||||
2: '2',
|
||||
3: '3',
|
||||
4: '4',
|
||||
5: '5',
|
||||
6: '6',
|
||||
7: '7',
|
||||
8: '8',
|
||||
9: '9',
|
||||
10: 'a',
|
||||
11: 'b',
|
||||
12: 'c',
|
||||
13: 'd',
|
||||
14: 'e',
|
||||
15: 'f',
|
||||
16: 'g',
|
||||
17: 'h',
|
||||
18: 'i',
|
||||
19: 'j',
|
||||
20: 'k',
|
||||
21: 'l',
|
||||
22: 'm',
|
||||
23: 'n',
|
||||
24: 'o',
|
||||
25: 'p',
|
||||
26: 'q',
|
||||
27: 'r',
|
||||
28: 's',
|
||||
29: 't',
|
||||
30: 'u',
|
||||
31: 'v',
|
||||
32: 'w',
|
||||
33: 'x',
|
||||
34: 'y',
|
||||
35: 'z'
|
||||
}
|
||||
|
||||
|
||||
def to_str_rep(num):
|
||||
if num.is_nan():
|
||||
return num.Js('NaN')
|
||||
elif num.is_infinity():
|
||||
sign = '-' if num.value < 0 else ''
|
||||
return num.Js(sign + 'Infinity')
|
||||
elif isinstance(num.value,
|
||||
(long, int)) or num.value.is_integer(): # dont print .0
|
||||
return num.Js(unicode(int(num.value)))
|
||||
return num.Js(unicode(num.value)) # accurate enough
|
||||
|
||||
|
||||
class NumberPrototype:
|
||||
def toString(radix):
|
||||
if this.Class != 'Number':
|
||||
raise this.MakeError('TypeError',
|
||||
'Number.prototype.valueOf is not generic')
|
||||
if radix.is_undefined():
|
||||
return to_str_rep(this)
|
||||
r = radix.to_int()
|
||||
if r == 10:
|
||||
return to_str_rep(this)
|
||||
if r not in xrange(2, 37):
|
||||
raise this.MakeError(
|
||||
'RangeError',
|
||||
'Number.prototype.toString() radix argument must be between 2 and 36'
|
||||
)
|
||||
num = this.to_int()
|
||||
if num < 0:
|
||||
num = -num
|
||||
sign = '-'
|
||||
else:
|
||||
sign = ''
|
||||
res = ''
|
||||
while num:
|
||||
s = RADIX_SYMBOLS[num % r]
|
||||
num = num // r
|
||||
res = s + res
|
||||
return sign + (res if res else '0')
|
||||
|
||||
def valueOf():
|
||||
if this.Class != 'Number':
|
||||
raise this.MakeError('TypeError',
|
||||
'Number.prototype.valueOf is not generic')
|
||||
return this.value
|
||||
|
||||
def toLocaleString():
|
||||
return this.to_string()
|
||||
|
||||
def toFixed(fractionDigits):
|
||||
if this.Class != 'Number':
|
||||
raise this.MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toFixed called on incompatible receiver')
|
||||
digs = fractionDigits.to_int()
|
||||
if digs < 0 or digs > 20:
|
||||
raise this.MakeError(
|
||||
'RangeError',
|
||||
'toFixed() digits argument must be between 0 and 20')
|
||||
elif this.is_infinity():
|
||||
return 'Infinity' if this.value > 0 else '-Infinity'
|
||||
elif this.is_nan():
|
||||
return 'NaN'
|
||||
return format(this.value, '-.%df' % digs)
|
||||
|
||||
def toExponential(fractionDigits):
|
||||
if this.Class != 'Number':
|
||||
raise this.MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toExponential called on incompatible receiver'
|
||||
)
|
||||
digs = fractionDigits.to_int()
|
||||
if digs < 0 or digs > 20:
|
||||
raise this.MakeError(
|
||||
'RangeError',
|
||||
'toFixed() digits argument must be between 0 and 20')
|
||||
elif this.is_infinity():
|
||||
return 'Infinity' if this.value > 0 else '-Infinity'
|
||||
elif this.is_nan():
|
||||
return 'NaN'
|
||||
return format(this.value, '-.%de' % digs)
|
||||
|
||||
def toPrecision(precision):
|
||||
if this.Class != 'Number':
|
||||
raise this.MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toPrecision called on incompatible receiver')
|
||||
if precision.is_undefined():
|
||||
return this.to_string()
|
||||
prec = precision.to_int()
|
||||
if this.is_nan():
|
||||
return 'NaN'
|
||||
elif this.is_infinity():
|
||||
return 'Infinity' if this.value > 0 else '-Infinity'
|
||||
digs = prec - len(str(int(this.value)))
|
||||
if digs >= 0:
|
||||
return format(this.value, '-.%df' % digs)
|
||||
else:
|
||||
return format(this.value, '-.%df' % (prec - 1))
|
||||
@@ -0,0 +1,28 @@
|
||||
class ObjectPrototype:
|
||||
def toString():
|
||||
return '[object %s]' % this.Class
|
||||
|
||||
def valueOf():
|
||||
return this.to_object()
|
||||
|
||||
def toLocaleString():
|
||||
return this.callprop('toString')
|
||||
|
||||
def hasOwnProperty(prop):
|
||||
return this.get_own_property(prop.to_string().value) is not None
|
||||
|
||||
def isPrototypeOf(obj):
|
||||
#a bit stupid specification but well
|
||||
# for example Object.prototype.isPrototypeOf.call((5).__proto__, 5) gives false
|
||||
if not obj.is_object():
|
||||
return False
|
||||
while 1:
|
||||
obj = obj.prototype
|
||||
if obj is None or obj.is_null():
|
||||
return False
|
||||
if obj is this:
|
||||
return True
|
||||
|
||||
def propertyIsEnumerable(prop):
|
||||
cand = this.own.get(prop.to_string().value)
|
||||
return cand is not None and cand.get('enumerable')
|
||||
@@ -0,0 +1,45 @@
|
||||
class RegExpPrototype:
|
||||
def toString():
|
||||
flags = u''
|
||||
try:
|
||||
if this.glob:
|
||||
flags += u'g'
|
||||
if this.ignore_case:
|
||||
flags += u'i'
|
||||
if this.multiline:
|
||||
flags += u'm'
|
||||
except:
|
||||
pass
|
||||
v = this.value if this.value else '(?:)'
|
||||
return u'/%s/' % v + flags
|
||||
|
||||
def test(string):
|
||||
return Exec(this, string) is not this.null
|
||||
|
||||
def exec2(string
|
||||
): # will be changed to exec in base.py. cant name it exec here
|
||||
return Exec(this, string)
|
||||
|
||||
|
||||
def Exec(this, string):
|
||||
if this.Class != 'RegExp':
|
||||
raise this.MakeError('TypeError',
|
||||
'RegExp.prototype.exec is not generic!')
|
||||
string = string.to_string()
|
||||
length = len(string)
|
||||
i = this.get('lastIndex').to_int() if this.glob else 0
|
||||
matched = False
|
||||
while not matched:
|
||||
if i < 0 or i > length:
|
||||
this.put('lastIndex', this.Js(0))
|
||||
return this.null
|
||||
matched = this.match(string.value, i)
|
||||
i += 1
|
||||
start, end = matched.span() #[0]+i-1, matched.span()[1]+i-1
|
||||
if this.glob:
|
||||
this.put('lastIndex', this.Js(end))
|
||||
arr = this.Js(
|
||||
[this.Js(e) for e in [matched.group()] + list(matched.groups())])
|
||||
arr.put('index', this.Js(start))
|
||||
arr.put('input', string)
|
||||
return arr
|
||||
@@ -0,0 +1,306 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .jsregexp import Exec
|
||||
import re
|
||||
DIGS = set('0123456789')
|
||||
WHITE = u"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
|
||||
|
||||
|
||||
def replacement_template(rep, source, span, npar):
|
||||
"""Takes the replacement template and some info about the match and returns filled template
|
||||
"""
|
||||
n = 0
|
||||
res = ''
|
||||
while n < len(rep) - 1:
|
||||
char = rep[n]
|
||||
if char == '$':
|
||||
if rep[n + 1] == '$':
|
||||
res += '$'
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] == '`':
|
||||
# replace with string that is BEFORE match
|
||||
res += source[:span[0]]
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] == '\'':
|
||||
# replace with string that is AFTER match
|
||||
res += source[span[1]:]
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] in DIGS:
|
||||
dig = rep[n + 1]
|
||||
if n + 2 < len(rep) and rep[n + 2] in DIGS:
|
||||
dig += rep[n + 2]
|
||||
num = int(dig)
|
||||
# we will not do any replacements if we dont have this npar or dig is 0
|
||||
if not num or num > len(npar):
|
||||
res += '$' + dig
|
||||
else:
|
||||
# None - undefined has to be replaced with ''
|
||||
res += npar[num - 1] if npar[num - 1] else ''
|
||||
n += 1 + len(dig)
|
||||
continue
|
||||
res += char
|
||||
n += 1
|
||||
if n < len(rep):
|
||||
res += rep[-1]
|
||||
return res
|
||||
|
||||
|
||||
###################################################
|
||||
|
||||
|
||||
class StringPrototype:
|
||||
def toString():
|
||||
if this.Class != 'String':
|
||||
raise this.MakeError('TypeError',
|
||||
'String.prototype.toString is not generic')
|
||||
return this.value
|
||||
|
||||
def valueOf():
|
||||
if this.Class != 'String':
|
||||
raise this.MakeError('TypeError',
|
||||
'String.prototype.valueOf is not generic')
|
||||
return this.value
|
||||
|
||||
def charAt(pos):
|
||||
this.cok()
|
||||
pos = pos.to_int()
|
||||
s = this.to_string()
|
||||
if 0 <= pos < len(s.value):
|
||||
char = s.value[pos]
|
||||
if char not in s.CHAR_BANK:
|
||||
s.Js(char) # add char to char bank
|
||||
return s.CHAR_BANK[char]
|
||||
return s.CHAR_BANK['']
|
||||
|
||||
def charCodeAt(pos):
|
||||
this.cok()
|
||||
pos = pos.to_int()
|
||||
s = this.to_string()
|
||||
if 0 <= pos < len(s.value):
|
||||
return s.Js(ord(s.value[pos]))
|
||||
return s.NaN
|
||||
|
||||
def concat():
|
||||
this.cok()
|
||||
s = this.to_string()
|
||||
res = s.value
|
||||
for e in arguments.to_list():
|
||||
res += e.to_string().value
|
||||
return res
|
||||
|
||||
def indexOf(searchString, position):
|
||||
this.cok()
|
||||
s = this.to_string().value
|
||||
search = searchString.to_string().value
|
||||
pos = position.to_int()
|
||||
return this.Js(s.find(search, min(max(pos, 0), len(s))))
|
||||
|
||||
def lastIndexOf(searchString, position):
|
||||
this.cok()
|
||||
s = this.to_string().value
|
||||
search = searchString.to_string().value
|
||||
pos = position.to_number()
|
||||
pos = 10**15 if pos.is_nan() else pos.to_int()
|
||||
return s.rfind(search, 0, min(max(pos, 0) + 1, len(s)))
|
||||
|
||||
def localeCompare(that):
|
||||
this.cok()
|
||||
s = this.to_string()
|
||||
that = that.to_string()
|
||||
if s < that:
|
||||
return this.Js(-1)
|
||||
elif s > that:
|
||||
return this.Js(1)
|
||||
return this.Js(0)
|
||||
|
||||
def match(regexp):
|
||||
this.cok()
|
||||
s = this.to_string()
|
||||
r = this.RegExp(regexp) if regexp.Class != 'RegExp' else regexp
|
||||
if not r.glob:
|
||||
return Exec(r, s)
|
||||
r.put('lastIndex', this.Js(0))
|
||||
found = []
|
||||
previous_last_index = 0
|
||||
last_match = True
|
||||
while last_match:
|
||||
result = Exec(r, s)
|
||||
if result.is_null():
|
||||
last_match = False
|
||||
else:
|
||||
this_index = r.get('lastIndex').value
|
||||
if this_index == previous_last_index:
|
||||
r.put('lastIndex', this.Js(this_index + 1))
|
||||
previous_last_index += 1
|
||||
else:
|
||||
previous_last_index = this_index
|
||||
matchStr = result.get('0')
|
||||
found.append(matchStr)
|
||||
if not found:
|
||||
return this.null
|
||||
return found
|
||||
|
||||
def replace(searchValue, replaceValue):
|
||||
# VERY COMPLICATED. to check again.
|
||||
this.cok()
|
||||
string = this.to_string()
|
||||
s = string.value
|
||||
res = ''
|
||||
if not replaceValue.is_callable():
|
||||
replaceValue = replaceValue.to_string().value
|
||||
func = False
|
||||
else:
|
||||
func = True
|
||||
# Replace all ( global )
|
||||
if searchValue.Class == 'RegExp' and searchValue.glob:
|
||||
last = 0
|
||||
for e in re.finditer(searchValue.pat, s):
|
||||
res += s[last:e.span()[0]]
|
||||
if func:
|
||||
# prepare arguments for custom func (replaceValue)
|
||||
args = (e.group(), ) + e.groups() + (e.span()[1], string)
|
||||
# convert all types to JS
|
||||
args = map(this.Js, args)
|
||||
res += replaceValue(*args).to_string().value
|
||||
else:
|
||||
res += replacement_template(replaceValue, s, e.span(),
|
||||
e.groups())
|
||||
last = e.span()[1]
|
||||
res += s[last:]
|
||||
return this.Js(res)
|
||||
elif searchValue.Class == 'RegExp':
|
||||
e = re.search(searchValue.pat, s)
|
||||
if e is None:
|
||||
return string
|
||||
span = e.span()
|
||||
pars = e.groups()
|
||||
match = e.group()
|
||||
else:
|
||||
match = searchValue.to_string().value
|
||||
ind = s.find(match)
|
||||
if ind == -1:
|
||||
return string
|
||||
span = ind, ind + len(match)
|
||||
pars = ()
|
||||
res = s[:span[0]]
|
||||
if func:
|
||||
args = (match, ) + pars + (span[1], string)
|
||||
# convert all types to JS
|
||||
this_ = this
|
||||
args = tuple([this_.Js(x) for x in args])
|
||||
res += replaceValue(*args).to_string().value
|
||||
else:
|
||||
res += replacement_template(replaceValue, s, span, pars)
|
||||
res += s[span[1]:]
|
||||
return res
|
||||
|
||||
def search(regexp):
|
||||
this.cok()
|
||||
string = this.to_string()
|
||||
if regexp.Class == 'RegExp':
|
||||
rx = regexp
|
||||
else:
|
||||
rx = this.RegExp(regexp)
|
||||
res = re.search(rx.pat, string.value)
|
||||
if res is not None:
|
||||
return this.Js(res.span()[0])
|
||||
return -1
|
||||
|
||||
def slice(start, end):
|
||||
this.cok()
|
||||
s = this.to_string()
|
||||
start = start.to_int()
|
||||
length = len(s.value)
|
||||
end = length if end.is_undefined() else end.to_int()
|
||||
#From = max(length+start, 0) if start<0 else min(length, start)
|
||||
#To = max(length+end, 0) if end<0 else min(length, end)
|
||||
return s.value[start:end]
|
||||
|
||||
def split(separator, limit):
|
||||
# its a bit different that re.split!
|
||||
this.cok()
|
||||
S = this.to_string()
|
||||
s = S.value
|
||||
lim = 2**32 - 1 if limit.is_undefined() else limit.to_uint32()
|
||||
if not lim:
|
||||
return []
|
||||
if separator.is_undefined():
|
||||
return [s]
|
||||
len_s = len(s)
|
||||
res = []
|
||||
R = separator if separator.Class == 'RegExp' else separator.to_string()
|
||||
if not len_s:
|
||||
if SplitMatch(s, 0, R) is None:
|
||||
return [S]
|
||||
return []
|
||||
p = q = 0
|
||||
while q != len_s:
|
||||
e, cap = SplitMatch(s, q, R)
|
||||
if e is None or e == p:
|
||||
q += 1
|
||||
continue
|
||||
res.append(s[p:q])
|
||||
p = q = e
|
||||
if len(res) == lim:
|
||||
return res
|
||||
for element in cap:
|
||||
res.append(this.Js(element))
|
||||
if len(res) == lim:
|
||||
return res
|
||||
res.append(s[p:])
|
||||
return res
|
||||
|
||||
def substring(start, end):
|
||||
this.cok()
|
||||
s = this.to_string().value
|
||||
start = start.to_int()
|
||||
length = len(s)
|
||||
end = length if end.is_undefined() else end.to_int()
|
||||
fstart = min(max(start, 0), length)
|
||||
fend = min(max(end, 0), length)
|
||||
return this.Js(s[min(fstart, fend):max(fstart, fend)])
|
||||
|
||||
def substr(start, length):
|
||||
#I hate this function and its description in specification
|
||||
r1 = this.to_string().value
|
||||
r2 = start.to_int()
|
||||
r3 = 10**20 if length.is_undefined() else length.to_int()
|
||||
r4 = len(r1)
|
||||
r5 = r2 if r2 >= 0 else max(0, r2 + r4)
|
||||
r6 = min(max(r3, 0), r4 - r5)
|
||||
if r6 <= 0:
|
||||
return ''
|
||||
return r1[r5:r5 + r6]
|
||||
|
||||
def toLowerCase():
|
||||
this.cok()
|
||||
return this.Js(this.to_string().value.lower())
|
||||
|
||||
def toLocaleLowerCase():
|
||||
this.cok()
|
||||
return this.Js(this.to_string().value.lower())
|
||||
|
||||
def toUpperCase():
|
||||
this.cok()
|
||||
return this.Js(this.to_string().value.upper())
|
||||
|
||||
def toLocaleUpperCase():
|
||||
this.cok()
|
||||
return this.Js(this.to_string().value.upper())
|
||||
|
||||
def trim():
|
||||
this.cok()
|
||||
return this.Js(this.to_string().value.strip(WHITE))
|
||||
|
||||
|
||||
def SplitMatch(s, q, R):
|
||||
# s is Py String to match, q is the py int match start and R is Js RegExp or String.
|
||||
if R.Class == 'RegExp':
|
||||
res = R.match(s, q)
|
||||
return (None, ()) if res is None else (res.span()[1], res.groups())
|
||||
# R is just a string
|
||||
if s[q:].startswith(R.value):
|
||||
return q + len(R.value), ()
|
||||
return None, ()
|
||||
@@ -0,0 +1,344 @@
|
||||
# this is based on jsarray.py
|
||||
|
||||
import six
|
||||
try:
|
||||
import numpy
|
||||
except:
|
||||
pass
|
||||
|
||||
if six.PY3:
|
||||
xrange = range
|
||||
import functools
|
||||
|
||||
|
||||
def to_arr(this):
|
||||
"""Returns Python array from Js array"""
|
||||
return [this.get(str(e)) for e in xrange(len(this))]
|
||||
|
||||
|
||||
ARR_STACK = set({})
|
||||
|
||||
|
||||
class TypedArrayPrototype:
|
||||
def toString():
|
||||
# this function is wrong
|
||||
func = this.get('join')
|
||||
if not func.is_callable():
|
||||
|
||||
@this.Js
|
||||
def func():
|
||||
return '[object %s]' % this.Class
|
||||
|
||||
return func.call(this, ())
|
||||
|
||||
def toLocaleString(locales=None, options=None):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
# separator is simply a comma ','
|
||||
if not arr_len:
|
||||
return ''
|
||||
res = []
|
||||
for i in xrange(arr_len):
|
||||
element = array[str(i)]
|
||||
if element.is_undefined() or element.is_null():
|
||||
res.append('')
|
||||
else:
|
||||
cand = element.to_object()
|
||||
str_func = element.get('toLocaleString')
|
||||
if not str_func.is_callable():
|
||||
raise this.MakeError(
|
||||
'TypeError',
|
||||
'toLocaleString method of item at index %d is not callable'
|
||||
% i)
|
||||
res.append(element.callprop('toLocaleString').value)
|
||||
return ','.join(res)
|
||||
|
||||
def join(separator):
|
||||
ARR_STACK.add(this)
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
separator = ',' if separator.is_undefined() else separator.to_string(
|
||||
).value
|
||||
elems = []
|
||||
for e in xrange(arr_len):
|
||||
elem = array.get(str(e))
|
||||
if elem in ARR_STACK:
|
||||
s = ''
|
||||
else:
|
||||
s = elem.to_string().value
|
||||
elems.append(
|
||||
s if not (elem.is_undefined() or elem.is_null()) else '')
|
||||
res = separator.join(elems)
|
||||
ARR_STACK.remove(this)
|
||||
return res
|
||||
|
||||
def reverse():
|
||||
array = this.to_object() # my own algorithm
|
||||
vals = to_arr(array)
|
||||
has_props = [array.has_property(str(e)) for e in xrange(len(array))]
|
||||
vals.reverse()
|
||||
has_props.reverse()
|
||||
for i, val in enumerate(vals):
|
||||
if has_props[i]:
|
||||
array.put(str(i), val)
|
||||
else:
|
||||
array.delete(str(i))
|
||||
return array
|
||||
|
||||
def slice(start, end): # todo check
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
relative_start = start.to_int()
|
||||
k = max((arr_len + relative_start), 0) if relative_start < 0 else min(
|
||||
relative_start, arr_len)
|
||||
relative_end = arr_len if end.is_undefined() else end.to_int()
|
||||
final = max((arr_len + relative_end), 0) if relative_end < 0 else min(
|
||||
relative_end, arr_len)
|
||||
res = []
|
||||
n = 0
|
||||
while k < final:
|
||||
pk = str(k)
|
||||
if array.has_property(pk):
|
||||
res.append(array.get(pk))
|
||||
k += 1
|
||||
n += 1
|
||||
return res
|
||||
|
||||
def sort(cmpfn):
|
||||
if not this.Class in ('Array', 'Arguments'):
|
||||
return this.to_object() # do nothing
|
||||
arr = []
|
||||
for i in xrange(len(this)):
|
||||
arr.append(this.get(six.text_type(i)))
|
||||
|
||||
if not arr:
|
||||
return this
|
||||
if not cmpfn.is_callable():
|
||||
cmpfn = None
|
||||
cmp = lambda a, b: sort_compare(a, b, cmpfn)
|
||||
if six.PY3:
|
||||
key = functools.cmp_to_key(cmp)
|
||||
arr.sort(key=key)
|
||||
else:
|
||||
arr.sort(cmp=cmp)
|
||||
for i in xrange(len(arr)):
|
||||
this.put(six.text_type(i), arr[i])
|
||||
|
||||
return this
|
||||
|
||||
def indexOf(searchElement):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if arr_len == 0:
|
||||
return -1
|
||||
if len(arguments) > 1:
|
||||
n = arguments[1].to_int()
|
||||
else:
|
||||
n = 0
|
||||
if n >= arr_len:
|
||||
return -1
|
||||
if n >= 0:
|
||||
k = n
|
||||
else:
|
||||
k = arr_len - abs(n)
|
||||
if k < 0:
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
elementK = array.get(str(k))
|
||||
if searchElement.strict_equality_comparison(elementK):
|
||||
return k
|
||||
k += 1
|
||||
return -1
|
||||
|
||||
def lastIndexOf(searchElement):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if arr_len == 0:
|
||||
return -1
|
||||
if len(arguments) > 1:
|
||||
n = arguments[1].to_int()
|
||||
else:
|
||||
n = arr_len - 1
|
||||
if n >= 0:
|
||||
k = min(n, arr_len - 1)
|
||||
else:
|
||||
k = arr_len - abs(n)
|
||||
while k >= 0:
|
||||
if array.has_property(str(k)):
|
||||
elementK = array.get(str(k))
|
||||
if searchElement.strict_equality_comparison(elementK):
|
||||
return k
|
||||
k -= 1
|
||||
return -1
|
||||
|
||||
def every(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
if not callbackfn.call(
|
||||
T, (kValue, this.Js(k), array)).to_boolean().value:
|
||||
return False
|
||||
k += 1
|
||||
return True
|
||||
|
||||
def some(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
if callbackfn.call(
|
||||
T, (kValue, this.Js(k), array)).to_boolean().value:
|
||||
return True
|
||||
k += 1
|
||||
return False
|
||||
|
||||
def forEach(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
callbackfn.call(T, (kValue, this.Js(k), array))
|
||||
k += 1
|
||||
|
||||
def map(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
A = this.Js([])
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
Pk = str(k)
|
||||
if array.has_property(Pk):
|
||||
kValue = array.get(Pk)
|
||||
mappedValue = callbackfn.call(T, (kValue, this.Js(k), array))
|
||||
A.define_own_property(
|
||||
Pk, {
|
||||
'value': mappedValue,
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
})
|
||||
k += 1
|
||||
return A
|
||||
|
||||
def filter(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = arguments[1]
|
||||
res = []
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
if callbackfn.call(
|
||||
T, (kValue, this.Js(k), array)).to_boolean().value:
|
||||
res.append(kValue)
|
||||
k += 1
|
||||
return res # converted to js array automatically
|
||||
|
||||
def reduce(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
if not arr_len and len(arguments) < 2:
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Reduce of empty array with no initial value')
|
||||
k = 0
|
||||
if len(arguments) > 1: # initial value present
|
||||
accumulator = arguments[1]
|
||||
else:
|
||||
kPresent = False
|
||||
while not kPresent and k < arr_len:
|
||||
kPresent = array.has_property(str(k))
|
||||
if kPresent:
|
||||
accumulator = array.get(str(k))
|
||||
k += 1
|
||||
if not kPresent:
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Reduce of empty array with no initial value')
|
||||
while k < arr_len:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
accumulator = callbackfn.call(
|
||||
this.undefined, (accumulator, kValue, this.Js(k), array))
|
||||
k += 1
|
||||
return accumulator
|
||||
|
||||
def reduceRight(callbackfn):
|
||||
array = this.to_object()
|
||||
arr_len = array.get("length").to_uint32()
|
||||
if not callbackfn.is_callable():
|
||||
raise this.MakeError('TypeError', 'callbackfn must be a function')
|
||||
if not arr_len and len(arguments) < 2:
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Reduce of empty array with no initial value')
|
||||
k = arr_len - 1
|
||||
if len(arguments) > 1: # initial value present
|
||||
accumulator = arguments[1]
|
||||
else:
|
||||
kPresent = False
|
||||
while not kPresent and k >= 0:
|
||||
kPresent = array.has_property(str(k))
|
||||
if kPresent:
|
||||
accumulator = array.get(str(k))
|
||||
k -= 1
|
||||
if not kPresent:
|
||||
raise this.MakeError(
|
||||
'TypeError', 'Reduce of empty array with no initial value')
|
||||
while k >= 0:
|
||||
if array.has_property(str(k)):
|
||||
kValue = array.get(str(k))
|
||||
accumulator = callbackfn.call(
|
||||
this.undefined, (accumulator, kValue, this.Js(k), array))
|
||||
k -= 1
|
||||
return accumulator
|
||||
|
||||
|
||||
def sort_compare(a, b, comp):
|
||||
if a is None:
|
||||
if b is None:
|
||||
return 0
|
||||
return 1
|
||||
if b is None:
|
||||
if a is None:
|
||||
return 0
|
||||
return -1
|
||||
if a.is_undefined():
|
||||
if b.is_undefined():
|
||||
return 0
|
||||
return 1
|
||||
if b.is_undefined():
|
||||
if a.is_undefined():
|
||||
return 0
|
||||
return -1
|
||||
if comp is not None:
|
||||
res = comp.call(a.undefined, (a, b))
|
||||
return res.to_int()
|
||||
x, y = a.to_string(), b.to_string()
|
||||
if x < y:
|
||||
return -1
|
||||
elif x > y:
|
||||
return 1
|
||||
return 0
|
||||
Reference in New Issue
Block a user