/*JSON{ "type" : "constructor", "class" : "Number", "name" : "Number", "generate" : "jswrap_number_constructor", "params" : [ ["value","JsVarArray","A single value to be converted to a number"] ], "return" : ["JsVar","A Number object"] } Creates a number */ JsVar *jswrap_number_constructor(JsVar *args) { if (jsvGetArrayLength(args)==0) return jsvNewFromInteger(0); JsVar *val = jsvGetArrayItem(args, 0); JsVar *result = 0; if (jsvIsArray(val)) { JsVarInt l = jsvGetArrayLength(val); if (l==0) result = jsvNewFromInteger(0); else if (l==1) { JsVar *n = jsvGetArrayItem(val, 0); if (jsvIsString(n) && jsvIsEmptyString(n)) result = jsvNewFromInteger(0); else if (!jsvIsBoolean(n)) result=jsvAsNumber(n); jsvUnLock(n); } // else NaN } else if (jsvIsUndefined(val) || jsvIsObject(val)) result = 0; else { if (jsvIsString(val) && jsvIsEmptyString(val)) { result = jsvNewFromInteger(0); } else result = jsvAsNumber(val); } jsvUnLock(val); if (result) return result; return jsvNewFromFloat(NAN); }
/*JSON{ "type" : "constructor", "class" : "Date", "name" : "Date", "generate" : "jswrap_date_constructor", "params" : [ ["args","JsVarArray","Either nothing (current time), one numeric argument (milliseconds since 1970), a date string (see `Date.parse`), or [year, month, day, hour, minute, second, millisecond] "] ], "return" : ["JsVar","A Date object"], "return_object" : "Date" } Creates a date object */ JsVar *jswrap_date_constructor(JsVar *args) { JsVarFloat time = 0; if (jsvGetArrayLength(args)==0) { time = jswrap_date_now(); } else if (jsvGetArrayLength(args)==1) { JsVar *arg = jsvGetArrayItem(args, 0); if (jsvIsNumeric(arg)) time = jsvGetFloat(arg); else if (jsvIsString(arg)) time = jswrap_date_parse(arg); else jsWarn("Variables of type %t are not supported in date constructor", arg); jsvUnLock(arg); } else { CalendarDate date; date.year = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 0)); date.month = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 1)); date.day = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 2)); TimeInDay td; td.daysSinceEpoch = fromCalenderDate(&date); td.hour = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 3)); td.min = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 4)); td.sec = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 5)); td.ms = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 6)); td.zone = 0; time = fromTimeInDay(&td); } return jswrap_date_from_milliseconds(time); }
/*JSON{ "type" : "staticmethod", "class" : "Trig", "name" : "setTrigger", "generate" : "jswrap_trig_setTrigger", "params" : [ ["num","int","The trigger number (0..7)"], ["pos","float","The position (in degrees) to fire the trigger at"], ["pins","JsVar","An array of pins to pulse (max 4)"], ["pulseLength","float","The time (in msec) to pulse for"] ] } Set a trigger for a certain point in the cycle */ void jswrap_trig_setTrigger(JsVarInt num, JsVarFloat position, JsVar *pins, JsVarFloat pulseLength) { TriggerStruct *trig = &mainTrigger; if (num<0 || num>=TRIGGER_TRIGGERS_COUNT) { jsWarn("Invalid trigger number\n"); return; } if (!jsvIsArray(pins)) { jsWarn("Second argument must be an array of pins\n"); return; } if (jsvGetArrayLength(pins) > TRIGGERPOINT_TRIGGERS_COUNT) { jsWarn("Too many pins in array\n"); return; } // convert from degrees to teeth position = wrapAround(((position - trig->keyPosition) * trig->teethTotal / 360), trig->teethTotal); TriggerPointStruct *tp = &trig->triggers[num]; tp->newTooth = (unsigned char)position; tp->newToothFraction = (unsigned char)((position - tp->tooth)*256); tp->pulseLength = jshGetTimeFromMilliseconds(pulseLength); int i, l=(int)jsvGetArrayLength(pins); for (i=0;i<TRIGGERPOINT_TRIGGERS_COUNT;i++) { tp->pins[i] = (Pin)((i<l) ? jshGetPinFromVarAndUnLock(jsvGetArrayItem(pins, i)) : PIN_UNDEFINED); } // now copy over data if we need to do it immediately if (tp->tooth==TRIGGERPOINT_TOOTH_DISABLE || tp->newTooth==TRIGGERPOINT_TOOTH_DISABLE) { tp->tooth = tp->newTooth; tp->toothFraction = tp->newToothFraction; } // all done! }
/*JSON{ "type" : "method", "ifndef" : "SAVE_ON_FLASH", "class" : "RegExp", "name" : "exec", "params" : [ ["str","JsVar","A string to match on"] ], "generate" : "jswrap_regexp_exec", "return" : ["JsVar","A result array, or null"] } Test this regex on a string - returns a result array on success, or `null` otherwise. `/Wo/.exec("Hello World")` will return: ``` [ "Wo", "index": 6, "input": "Hello World" ] ``` Or with groups `/W(o)rld/.exec("Hello World")` returns: ``` [ "World", "o", "index": 6, "input": "Hello World" ] ``` */ JsVar *jswrap_regexp_exec(JsVar *parent, JsVar *arg) { JsVar *str = jsvAsString(arg); JsVarInt lastIndex = jsvGetIntegerAndUnLock(jsvObjectGetChild(parent, "lastIndex", 0)); JsVar *regex = jsvObjectGetChild(parent, "source", 0); if (!jsvIsString(regex)) { jsvUnLock2(str,regex); return 0; } size_t regexLen = jsvGetStringLength(regex); char *regexPtr = (char *)alloca(regexLen+1); if (!regexPtr) { jsvUnLock2(str,regex); return 0; } jsvGetString(regex, regexPtr, regexLen+1); jsvUnLock(regex); JsVar *rmatch = match(regexPtr, str, (size_t)lastIndex, jswrap_regexp_hasFlag(parent,'i')); jsvUnLock(str); if (!rmatch) { rmatch = jsvNewWithFlags(JSV_NULL); lastIndex = 0; } else { // if it's global, set lastIndex if (jswrap_regexp_hasFlag(parent,'g')) { JsVar *matchStr = jsvGetArrayItem(rmatch,0); lastIndex = jsvGetIntegerAndUnLock(jsvObjectGetChild(rmatch, "index", 0)) + (JsVarInt)jsvGetStringLength(matchStr); jsvUnLock(matchStr); } else lastIndex = 0; } jsvObjectSetChildAndUnLock(parent, "lastIndex", jsvNewFromInteger(lastIndex)); return rmatch; }
/*JSON{ "type" : "method", "class" : "SPI", "name" : "write", "generate" : "jswrap_spi_write", "params" : [ ["data","JsVarArray",["One or more items to write. May be ints, strings, arrays, or objects of the form `{data: ..., count:#}`.","If the last argument is a pin, it is taken to be the NSS pin"]] ] } Write a character or array of characters to SPI - without reading the result back. For maximum speeds, please pass either Strings or Typed Arrays as arguments. */ void jswrap_spi_write(JsVar *parent, JsVar *args) { NOT_USED(parent); IOEventFlags device = jsiGetDeviceFromClass(parent); spi_sender spiSend; spi_sender_data spiSendData; if (!jsspiGetSendFunction(parent, &spiSend, &spiSendData)) return; Pin nss_pin = PIN_UNDEFINED; // If the last value is a pin, use it as the NSS pin JsVarInt len = jsvGetArrayLength(args); if (len>0) { JsVar *last = jsvGetArrayItem(args, len-1); // look at the last value if (jsvIsPin(last)) { nss_pin = jshGetPinFromVar(last); jsvUnLock(jsvArrayPop(args)); } jsvUnLock(last); } // we're only sending (no receive) if (DEVICE_IS_SPI(device)) jshSPISetReceive(device, false); // assert NSS if (nss_pin!=PIN_UNDEFINED) jshPinOutput(nss_pin, false); // Write data jsvIterateCallback(args, (void (*)(int, void *))spiSend, &spiSendData); // Wait until SPI send is finished, and flush data if (DEVICE_IS_SPI(device)) jshSPIWait(device); // de-assert NSS if (nss_pin!=PIN_UNDEFINED) jshPinOutput(nss_pin, true); }
/*JSON{ "type" : "method", "class" : "String", "name" : "match", "generate" : "jswrap_string_match", "params" : [ ["subStr","JsVar","Substring or RegExp to match"] ], "return" : ["JsVar","This match array"] } Matches `subStr` occurrence in the string. */ JsVar *jswrap_string_match(JsVar *parent, JsVar *subStr) { if (!jsvIsString(parent)) return 0; if (jsvIsUndefined(subStr)) return 0; #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(subStr, "RegExp")) { jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); JsVar *match; match = jswrap_regexp_exec(subStr, parent); if (!jswrap_regexp_hasFlag(subStr,'g')) { return match; } // global JsVar *array = jsvNewEmptyArray(); if (!array) return 0; // out of memory while (match && !jsvIsNull(match)) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); int last = idx+len; jsvArrayPushAndUnLock(array, matchStr); // search again jsvUnLock(match); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(last)); match = jswrap_regexp_exec(subStr, parent); } jsvUnLock(match); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); return array; } #endif subStr = jsvAsString(subStr); int idx = jswrap_string_indexOf(parent, subStr, 0, false); if (idx>=0) { JsVar *array = jsvNewEmptyArray(); if (!array) { jsvUnLock(subStr); return 0; // out of memory } jsvArrayPush(array, subStr); jsvObjectSetChildAndUnLock(array, "index", jsvNewFromInteger(idx)); jsvObjectSetChildAndUnLock(array, "input", subStr); return array; } jsvUnLock(subStr); return NULL; }
/*JSON{ "type" : "constructor", "class" : "Array", "name" : "Array", "generate" : "jswrap_array_constructor", "params" : [ ["args","JsVarArray","The length of the array OR any number of items to add to the array"] ], "return" : ["JsVar","An Array"] } Create an Array. Either give it one integer argument (>=0) which is the length of the array, or any number of arguments */ JsVar *jswrap_array_constructor(JsVar *args) { assert(args); if (jsvGetArrayLength(args)==1) { JsVar *firstArg = jsvSkipNameAndUnLock(jsvGetArrayItem(args,0)); if (jsvIsInt(firstArg) && jsvGetInteger(firstArg)>=0) { JsVarInt count = jsvGetInteger(firstArg); if (count>0) { JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; // out of memory jsvSetArrayLength(arr, count, false); jsvUnLock(firstArg); return arr; } } jsvUnLock(firstArg); } // Otherwise, we just return the array! return jsvLockAgain(args); }
/*JSON{ "type" : "constructor", "class" : "Array", "name" : "Array", "generate" : "jswrap_array_constructor", "params" : [ ["args","JsVarArray","The length of the array OR any number of items to add to the array"] ], "return" : ["JsVar","An Array"] } Create an Array. Either give it one integer argument (>=0) which is the length of the array, or any number of arguments */ JsVar *jswrap_array_constructor(JsVar *args) { assert(args); if (jsvGetArrayLength(args)==1) { JsVar *firstArg = jsvSkipNameAndUnLock(jsvGetArrayItem(args,0)); if (jsvIsNumeric(firstArg)) { JsVarFloat f = jsvGetFloat(firstArg); JsVarInt count = jsvGetInteger(firstArg); jsvUnLock(firstArg); if (f!=count || count<0) { jsExceptionHere(JSET_ERROR, "Invalid array length"); return 0; } else { JsVar *arr = jsvNewEmptyArray(); if (!arr) return 0; // out of memory jsvSetArrayLength(arr, count, false); return arr; } } else { jsvUnLock(firstArg); } } // Otherwise, we just return the array! return jsvLockAgain(args); }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { size_t length = (size_t)jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); bool needNewLine = false; size_t i; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); for (i=0;i<length && !jspIsInterrupted();i++) { if (!limited || i<JSON_LIMITED_AMOUNT || i>=length-JSON_LIMITED_AMOUNT) { if (i>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && i==length-JSON_LIMITED_AMOUNT) { if (needNewLine) jsonNewLine(nflags, user_callback, user_data); cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); } JsVar *item = jsvGetArrayItem(var, (JsVarInt)i); if (jsvIsUndefined(item) && (flags&JSON_NO_UNDEFINED)) item = jsvNewWithFlags(JSV_NULL); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; } jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; jsvUnLock(item); } } if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); if (allZero) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { cbprintf(user_callback, user_data, "new %s([", jswGetBasicObjectName(var)); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, "])"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool first = true; bool needNewLine = false; size_t sinceNewLine = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); while (jsvObjectIteratorHasValue(&it) && !jspIsInterrupted()) { JsVar *index = jsvObjectIteratorGetKey(&it); JsVar *item = jsvObjectIteratorGetValue(&it); bool hidden = jsvIsInternalObjectKey(index) || ((flags & JSON_IGNORE_FUNCTIONS) && jsvIsFunction(item)) || ((flags&JSON_NO_UNDEFINED) && jsvIsUndefined(item)); if (!hidden) { sinceNewLine++; if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if ((flags&JSON_NEWLINES) && sinceNewLine>JSON_ITEMS_ON_LINE_OBJECT) needNewLine = true; if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; sinceNewLine = 0; } cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"%q: ":"%q:", index); if (first) first = false; jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; } jsvUnLock(index); jsvUnLock(item); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock(var1); jsvUnLock(var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }
/*JSON{ "type" : "constructor", "class" : "String", "name" : "String", "generate" : "jswrap_string_constructor", "params" : [ ["str","JsVarArray","A value to turn into a string. If undefined or not supplied, an empty String is created."] ], "return" : ["JsVar","A String"] } Create a new String */ JsVar *jswrap_string_constructor(JsVar *args) { if (jsvGetArrayLength(args)==0) return jsvNewFromEmptyString(); // no argument - return an empty string return jsvAsString(jsvGetArrayItem(args, 0), true); }
/*JSON{ "type" : "method", "class" : "String", "name" : "split", "generate" : "jswrap_string_split", "params" : [ ["separator","JsVar","The separator `String` or `RegExp` to use"] ], "return" : ["JsVar","Part of this string from start for len characters"] } Return an array made by splitting this string up by the separator. eg. ```'1,2,3'.split(',')==['1', '2', '3']``` Regular Expressions can also be used to split strings, eg. `'1a2b3 4'.split(/[^0-9]/)==['1', '2', '3', '4']`. */ JsVar *jswrap_string_split(JsVar *parent, JsVar *split) { if (!jsvIsString(parent)) return 0; JsVar *array = jsvNewEmptyArray(); if (!array) return 0; // out of memory if (jsvIsUndefined(split)) { jsvArrayPush(array, parent); return array; } #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(split, "RegExp")) { unsigned int last = 0; JsVar *match; jsvObjectSetChildAndUnLock(split, "lastIndex", jsvNewFromInteger(0)); match = jswrap_regexp_exec(split, parent); while (match && !jsvIsNull(match)) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); jsvUnLock(matchStr); // do the replacement jsvArrayPushAndUnLock(array, jsvNewFromStringVar(parent, (size_t)last, (size_t)(idx-last))); last = idx+len; // search again jsvUnLock(match); jsvObjectSetChildAndUnLock(split, "lastIndex", jsvNewFromInteger(last)); match = jswrap_regexp_exec(split, parent); } jsvUnLock(match); jsvObjectSetChildAndUnLock(split, "lastIndex", jsvNewFromInteger(0)); // add remaining string after last match if (last<=jsvGetStringLength(parent)) jsvArrayPushAndUnLock(array, jsvNewFromStringVar(parent, (size_t)last, JSVAPPENDSTRINGVAR_MAXLENGTH)); return array; } #endif split = jsvAsString(split); int idx, last = 0; int splitlen = jsvIsUndefined(split) ? 0 : (int)jsvGetStringLength(split); int l = (int)jsvGetStringLength(parent) + 1 - splitlen; for (idx=0;idx<=l;idx++) { if (splitlen==0 && idx==0) continue; // special case for where split string is "" if (idx==l || splitlen==0 || jsvCompareString(parent, split, (size_t)idx, 0, true)==0) { if (idx==l) { idx=l+splitlen; // if the last element, do to the end of the string if (splitlen==0) break; } JsVar *part = jsvNewFromStringVar(parent, (size_t)last, (size_t)(idx-last)); if (!part) break; // out of memory jsvArrayPush(array, part); jsvUnLock(part); last = idx+splitlen; } } jsvUnLock(split); return array; }
/*JSON{ "type" : "method", "class" : "String", "name" : "replace", "generate" : "jswrap_string_replace", "params" : [ ["subStr","JsVar","The string to search for"], ["newSubStr","JsVar","The string to replace it with"] ], "return" : ["JsVar","This string with `subStr` replaced"] } Search and replace ONE occurrance of `subStr` with `newSubStr` and return the result. This doesn't alter the original string. Regular expressions not supported. */ JsVar *jswrap_string_replace(JsVar *parent, JsVar *subStr, JsVar *newSubStr) { JsVar *str = jsvAsString(parent); #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(subStr, "RegExp")) { JsVar *replace; if (jsvIsFunction(newSubStr) || jsvIsString(newSubStr)) replace = jsvLockAgain(newSubStr); else replace = jsvAsString(newSubStr); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); bool global = jswrap_regexp_hasFlag(subStr,'g'); JsVar *match; match = jswrap_regexp_exec(subStr, str); while (match && !jsvIsNull(match) && !jspIsInterrupted()) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); // do the replacement JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); JsvStringIterator dst; jsvStringIteratorNew(&dst, newStr, 0); jsvStringIteratorGotoEnd(&dst); if (jsvIsFunction(replace)) { unsigned int argCount = 0; JsVar *args[13]; args[argCount++] = jsvLockAgain(matchStr); JsVar *v; while ((v = jsvGetArrayItem(match, (JsVarInt)argCount))) args[argCount++] = v; args[argCount++] = jsvObjectGetChild(match,"index",0); args[argCount++] = jsvObjectGetChild(match,"input",0); JsVar *result = jsvAsStringAndUnLock(jspeFunctionCall(replace, 0, 0, false, (JsVarInt)argCount, args)); jsvUnLockMany(argCount, args); jsvStringIteratorAppendString(&dst, result, 0); jsvUnLock(result); } else { JsvStringIterator src; jsvStringIteratorNew(&src, replace, 0); while (jsvStringIteratorHasChar(&src)) { char ch = jsvStringIteratorGetChar(&src); if (ch=='$') { jsvStringIteratorNext(&src); ch = jsvStringIteratorGetChar(&src); JsVar *group = 0; if (ch>'0' && ch<='9') group = jsvGetArrayItem(match, ch-'0'); if (group) { jsvStringIteratorAppendString(&dst, group, 0); jsvUnLock(group); } else { jsvStringIteratorAppend(&dst, '$'); jsvStringIteratorAppend(&dst, ch); } } else { jsvStringIteratorAppend(&dst, ch); } jsvStringIteratorNext(&src); } jsvStringIteratorFree(&src); } JsVarInt lastIndex = 1+(JsVarInt)jsvStringIteratorGetIndex(&dst); jsvStringIteratorAppendString(&dst, str, (size_t)(idx+len)); jsvStringIteratorFree(&dst); jsvUnLock2(str,matchStr); str = newStr; // search again if global jsvUnLock(match); match = 0; if (global) { jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(lastIndex)); match = jswrap_regexp_exec(subStr, str); } } jsvUnLock(match); jsvUnLock(replace); // reset lastIndex if global if (global) jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); return str; } #endif newSubStr = jsvAsString(newSubStr); subStr = jsvAsString(subStr); int idx = jswrap_string_indexOf(parent, subStr, 0, false); if (idx>=0) { JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); jsvAppendStringVar(newStr, newSubStr, 0, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvAppendStringVar(newStr, str, (size_t)idx+jsvGetStringLength(subStr), JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(str); str = newStr; } jsvUnLock2(subStr, newSubStr); return str; }