PageSourceSearch

https://www.aceba.cat/comu/js/ostynscormtime.js

js aceba.cat collected 2026-10-03 20:28:17 UTC 13,355 bytes, 454 lines download raw bytes

1/* This reusable script is copyrighted.
2   Copyright (c) 2004,2005,2006 Claude Ostyn
3This script is free for use with attribution
4under the Creative Commons Attribution-ShareAlike 2.5 License.
5To view a copy of this license, visit
6http://creativecommons.org/licenses/by-sa/2.5/
7or send a letter to
8Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
9
10For any other use, contact Claude Ostyn via [email protected].
11
12USE AT YOUR OWN RISK!
13THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER
17BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20SOFTWARE.
21*/
22
23function centisecsToISODuration(n, bPrecise)
24{
25  // Note: SCORM and IEEE 1484.11.1 require centisec precision
26  // Parameters:
27  // n = number of centiseconds
28  // bPrecise = optional parameter; if true, duration will
29  // be expressed without using year and/or month fields.
30  // If bPrecise is not true, and the duration is long,
31  // months are calculated by approximation based on average number
32  // of days over 4 years (365*4+1), not counting the extra days
33  // for leap years. If a reference date was available,
34  // the calculation could be more precise, but becomes complex,
35  // since the exact result depends on where the reference date
36  // falls within the period (e.g. beginning, end or ???)
37  // 1 year ~ (365*4+1)/4*60*60*24*100 = 3155760000 centiseconds
38  // 1 month ~ (365*4+1)/48*60*60*24*100 = 262980000 centiseconds
39  // 1 day = 8640000 centiseconds
40  // 1 hour = 360000 centiseconds
41  // 1 minute = 6000 centiseconds
42  var str = "P";
43  var nCs=n;
44  var nY=0, nM=0, nD=0, nH=0, nMin=0, nS=0;
45  n = Math.max(n,0); // there is no such thing as a negative duration
46  var nCs = n;
47  // Next set of operations uses whole seconds
48  with (Math)
49  {
50    nCs = round(nCs);
51    if (bPrecise == true)
52    {
53      nD = floor(nCs / 8640000);
54    }
55    else
56    {
57      nY = floor(nCs / 3155760000);
58      nCs -= nY * 3155760000;
59      nM = floor(nCs / 262980000);
60      nCs -= nM * 262980000;
61      nD = floor(nCs / 8640000);
62    }
63    nCs -= nD * 8640000;
64    nH = floor(nCs / 360000);
65    nCs -= nH * 360000;
66    var nMin = floor(nCs /6000);
67    nCs -= nMin * 6000
68  }
69  // Now we can construct string
70  if (nY > 0) str += nY + "Y";
71  if (nM > 0) str += nM + "M";
72  if (nD > 0) str += nD + "D";
73  if ((nH > 0) || (nMin > 0) || (nCs > 0))
74  {
75    str += "T";
76    if (nH > 0) str += nH + "H";
77    if (nMin > 0) str += nMin + "M";
78    if (nCs > 0) str += (nCs / 100) + "S";
79  }
80  if (str == "P") str = "PT0H0M0S";
81  // technically PT0S should do but SCORM test suite assumes longer form.
82  return str;
83}
84
85
86function ISODurationToCentisec(str)
87{
88  // Only gross syntax check is performed here
89  // Months calculated by approximation based on average number
90  // of days over 4 years (365*4+1), not counting the extra days
91  // in leap years. If a reference date was available,
92  // the calculation could be more precise, but becomes complex,
93  // since the exact result depends on where the reference date
94  // falls within the period (e.g. beginning, end or ???)
95  // 1 year ~ (365*4+1)/4*60*60*24*100 = 3155760000 centiseconds
96  // 1 month ~ (365*4+1)/48*60*60*24*100 = 262980000 centiseconds
97  // 1 day = 8640000 centiseconds
98  // 1 hour = 360000 centiseconds
99  // 1 minute = 6000 centiseconds
100  var aV = new Array(0,0,0,0,0,0);
101  var bErr = false;
102  var bTFound = false;
103  if (str.indexOf("P") != 0) bErr = true;
104  if (!bErr)
105  {
106    var aT = new Array("Y","M","D","H","M","S")
107    var p=0, i=0;
108    str = str.substr(1); //get past the P
109    for (i = 0 ; i < aT.length; i++)
110    {
111      if (str.indexOf("T") == 0)
112      {
113        str = str.substr(1);
114        i = Math.max(i,3);
115        bTFound = true;
116      }
117      p = str.indexOf(aT[i]);
118      //alert("Checking for " + aT[i] + "\nstr = " + str);
119      if (p > -1)
120      {
121        // Is this a M before or after T?
122        if ((i == 1) && (str.indexOf("T") > -1) && (str.indexOf("T") < p)) continue;
123        if (aT[i] == "S")
124        {
125          aV[i] = parseFloat(str.substr(0,p))
126        }
127        else
128        {
129          aV[i] = parseInt(str.substr(0,p))
130        }
131        if (isNaN(aV[i]))
132        {
133          bErr = true;
134          break;
135        }
136        else if ((i > 2) && (!bTFound))
137        {
138          bErr = true;
139          break;
140        }
141        str = str.substr(p+1);
142      }
143    }
144    if ((!bErr) && (str.length != 0)) bErr = true;
145    //alert(aV.toString())
146  }
147  if (bErr)
148  {
149    //alert("Bad format: " + str)
150    return
151  }
152  return aV[0]*3155760000 + aV[1]*262980000
153    + aV[2]*8640000 + aV[3]*360000 + aV[4]*6000
154    + Math.round(aV[5]*100)
155}
156
157// Legacy functions to translate to/from SCORM 1.2 format
158
159function SCORM12DurationToCs(str)
160{
161  // Format is [HH]HH:MM:SS[.SS] or maybe sometimes MM:SS[.SS]
162  // Does not catch all possible errors
163  // First convert to centisecs
164  var a=str.split(":");
165  var nS=0, n=0;
166  var nMult = 1;
167  var bErr = ((a.length < 2) || (a.length > 3));
168  if (!bErr)
169  {
170    for (i=a.length-1;i >= 0; i--)
171    {
172      n = parseFloat(a[i]);
173      if (isNaN(n))
174      {
175        bErr = true;
176        break;
177      }
178      nS += n * nMult;
179      nMult *= 60;
180    }
181  }
182  if (bErr)
183  {
184    alert ("Incorrect format: " + str + "\n\nFormat must be [HH]HH:MM:SS[.SS]");
185    return NaN;
186  }
187  return Math.round(nS * 100);
188}
189
190function centisecsToSCORM12Duration(n)
191{
192  // Format is [HH]HH:MM:SS[.SS]
193  var bTruncated = false;
194  with (Math)
195  {
196    n = round(n);
197    var nH = floor(n / 360000);
198    var nCs = n - nH * 360000;
199    var nM = floor(nCs / 6000);
200    nCs = nCs - nM * 6000;
201    var nS = floor(nCs / 100);
202    nCs = nCs - nS * 100;
203  }
204  if (nH > 9999)
205  {
206    nH = 9999;
207    bTruncated = true;
208  }
209  var str = "0000" + nH + ":";
210  str = str.substr(str.length-5,5);
211  if (nM < 10) str += "0";
212  str += nM + ":";
213  if (nS < 10) str += "0";
214  str += nS;
215  if (nCs > 0)
216  {
217    str += ".";
218    if (nCs < 10) str += "0";
219    str += nCs;
220  }
221  //if (bTruncated) alert ("Hours truncated to 9999 to fit HHHH:MM:SS.SS format")
222  return str;
223}
224
225
226/*** time stamp helper function. Returns a time stamp in ISO format ***/
227
228function MakeISOtimeStamp(objSrcDate, bRelative, nResolution)
229{
230  // Make an ISO 8601 time stamp string as specified for SCORM 2004
231  // * objDate is an optional ECMAScript Date object;
232  //   if objDate is null, "this instant" is assumed.
233  // * bRelative is optional; if bRelative is true,
234  //   the time stamp will show local time with a time offset from UTC;
235  //   otherwise the time stamp will show UTC (a.k.a. Zulu) time.
236  // * nResolution is optional; it specifies max decimal digits
237  //   for fractions of second; it can be null, 0 or 2. If null, 2 is assumed.
238  var s = "";
239  var nY=0, nM=0, nD=0, nH=0, nMin=0, nS=0, nMs=0, nCs = 0;
240  var bCentisecs =  ((isNaN(nResolution)) || (nResolution != 0));
241  // Need to make a copy of the source date object because we will
242  // tweak it if we need to round up to next second
243  objDate = new Date();
244  with (objDate)
245  {
246    setTime(objSrcDate.getTime());
247    ((bRelative)? nMs = getMilliseconds(): nMs = getUTCMilliseconds());
248
249    if (bCentisecs)
250    {
251      // Default precision is centisecond. Let us see whether we need to add
252      // a rounding up adjustment
253      if (nMs > 994)
254      {
255        ((bRelative)? setMilliseconds(1000): setUTCMilliseconds(1000));
256      }
257      else
258      {
259        nCs = Math.floor(nMs / 10);
260      }
261    }
262    else
263    {
264      // Precision is whole seconds; round up if necessary
265      if (nMs > 499)
266      {
267        ((bRelative)? setMilliseconds(1000): setUTCMilliseconds(1000));
268      }
269    }
270    if (bRelative)
271    {
272      nY = getFullYear();
273      nM = getMonth();
274      nD = getDate();
275      nH = getHours();
276      nMin = getMinutes();
277      nS = getSeconds();
278    }
279    else
280    {
281      nY = getUTCFullYear();
282      nM = getUTCMonth();
283      nD = getUTCDate();
284      nH = getUTCHours();
285      nMin = getUTCMinutes();
286      nS = getUTCSeconds();
287    }
288  }
289  // Note: Date.Month() and Date.UTCMonth() are base 0 not 1
290  s = nY + "-" +
291    ZeroPad(nM+1, 2) + "-" +
292    ZeroPad(nD, 2) + "T" +
293    ZeroPad(nH, 2) + ":" +
294    ZeroPad(nMin, 2) + ":" +
295    ZeroPad(nS,2);
296  if (nCs > 0)
297  {
298    s += "." + ZeroPad(nCs,2);
299  }
300  if (bRelative)
301  {
302    // Need to flip the sign of the time zone offset
303    var nTZOff = -objDate.getTimezoneOffset();
304    if (nTZOff >= 0) s += "+";
305    s += ZeroPad(Math.round(nTZOff / 60), 2);
306    nTZOff = nTZOff % 60;
307    if (nTZOff > 0) s += ":" +  ZeroPad(nTZOff, 2);
308  }
309  else
310  {
311    s += "Z";
312  }
313  return s;
314}
315
316function ZeroPad(n, nLength)
317{
318  // Takes a number and pads it with leading 0 to the length specified.
319  // The padded length does not include negative sign if present.
320  var bNeg = (n < 0);
321  var s = n.toString();
322  if (bNeg) s = s.substr(1,s.length);
323  while (s.length < nLength) s = "0" + s;
324  if (bNeg) s = "-" + s;
325  return s
326}
327
328var gsParseErr = "";
329function DateFromISOString(strDate)
330{
331  // Convert an ISO 8601 formatted string to a local date
332  // Returns an ECMAScript Date object or null if an error was detected
333  // Assumes that the string is well formed and SCORM conformant
334  // otherwise a runtime error may occur in this function.
335  // In practice the data range is limited to the date range supported
336  // by the ECMAScript Date object. See the ECMAScript standard for details.
337  var sDate = strDate; // The date part of the input, after a little massaging
338  var sTime = null; // The time part of the input, if it is included
339  var sTimeOffset = null; // UTC offset, if specified in the input string
340  var sUTCOffsetSign = "";
341  var a = null;
341 // Will be reused for all kinds of string splits
342  var n=0, nY=0, nM=0, nD=1, nH=0, nMin=0, nS=0, nMs = 0;
343
344  gsParseErr = "";
345
346  // If this is "Zulu" time, it will make things a little easier
347  var bZulu = (sDate.indexOf("Z") > -1);
348  if (bZulu) sDate = sDate.substr(0, sDate.length - 1);
349
350  // Parse the ISO string into date and time
351  if (sDate.indexOf("T") > -1)
352  {
353    var a = sDate.split("T");
354    sDate = a[0];
355    var sTime = a[1];
356  }
357  // Parse the date part
358  a = sDate.split("-");
359  nY = parseInt(a[0],10);
360  if ((isNaN(nY)) || (nY > 9999) || (nY < 0000))
361  {
362    gsParseErr = "Invalid year value:\n" +  strDate;
363    return null;
364  }
365  if (a.length > 1)
366  {
367    nM = parseInt(a[1],10) - 1; // months are in base 0
368    if (nM < 0) alert("a[1] =" + a[1] + " from " + strDate);
369    if (a.length > 2)
370    {
371      nD = parseInt(a[2],10); // days are in base 1
372    }
373  }
374  // Done with the date. If there is a time part, parse it out.
375  if (sTime)
376  {
377    if (sTime.indexOf("-") > -1) sUTCOffsetSign = "-";
378    if (sTime.indexOf("+") > -1) sUTCOffsetSign = "+";
379    if (sUTCOffsetSign != "")
380    {
381      if (bZulu)
382      {
383        gsParseErr = "You can't have both UTC offset and Zulu in ISO time stamp:\n" + strDate;
384         return null;
385      }
386      a = sTime.split(sUTCOffsetSign);
387      sTime = a[0];
388      sTimeOffset = a[1];
389    }
390    a = sTime.split(":");
391    nH = parseInt(a[0],10);
392    if (a.length > 1)
393    {
394      nMin = parseInt(a[1],10);
395      if (a.length > 2)
396      {
397        (a[2].indexOf(".")<0?nS = parseInt(a[2],10) : nS = parseFloat(a[2]));
398        if (isNaN(nS))
399        {
400          gsParseErr = "Error parsing seconds: " + a[2] + "\n" + strDate;
401          return null;
402        }
403        nMs = Math.round((nS % 1) * 1000);
404        nS = Math.floor(nS);
405      }
406    }
407  }
408  else if (bZulu)
409  {
410    gsParseErr = "UTC not allowed in time stamp unless there is a Time part:\n" +
411      strDate;
412    return null;
413  }
414  var objDate = new Date();
415  if (bZulu)
416  {
417    objDate.setUTCFullYear(nY,nM,nD);
418    objDate.setUTCHours(nH,nMin,nS,nMs);
419  }
420  else
421  {
422    // Calculate and apply the time offset for local time
423    if (sTimeOffset)
424    {
425      var nOffset = 0;
426      a = sTimeOffset.split(":");
427      nOffset = parseInt(a[0]);
428      if (isNaN(nOffset))
429      {
430        gsParseErr = "Found UTC time offset but offset value is NaN:\n" + strDate;
431        return null;
432      }
433      nOffset = nOffset * 60
434      if (a.length > 1)
435      {
436        n = parseInt(a[1]);
437        if (isNaN(n))
438        {
439          gsParseErr = "Found UTC time offset minutes but minute value is NaN:\n" +
440            strDate;
441          return null;
442        }
443        nOffset += n;
444      }
445      nOffset = nOffset * 60; // minutes to milliseconds
446      if (sUTCOffsetSign == "-") nOffset = -nOffset;
447      objDate.setTime(objDate.getTime() + nOffset);
448    }
449    objDate.setFullYear(nY,nM,nD);
450    objDate.setHours(nH,nMin,nS,nMs);
451  }
452  return objDate
453}
454

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.