p2jsres.pp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. unit p2jsres;
  2. {$mode objfpc}
  3. {$h+}
  4. {$modeswitch externalclass}
  5. interface
  6. uses types;
  7. Type
  8. TResourceSource = (rsJS,rsHTML);
  9. TResourceInfo = record
  10. name : string;
  11. encoding : string;
  12. resourceunit : string;
  13. format : string;
  14. data : string;
  15. end;
  16. TResourceEnumCallBack = Reference to function(const resName : string) : boolean;
  17. TResourcesLoadedEnumCallBack = Reference to Procedure(const LoadedResources : Array of String);
  18. TResourcesLoadErrorCallBack = Reference to Procedure(const aError : string);
  19. Function SetResourceSource(aSource : TResourceSource) : TResourceSource;
  20. Function GetResourceNames : TStringDynArray;
  21. Function GetResourceNames(aSource : TResourceSource) : TStringDynArray;
  22. Function EnumResources(aCallback : TResourceEnumCallBack) : Integer;
  23. Function EnumResources(aSource : TResourceSource; aCallback : TResourceEnumCallBack) : Integer;
  24. Function GetResourceInfo(Const aName : String; var aInfo : TResourceInfo) : Boolean;
  25. Function GetResourceInfo(aSource : TResourceSource; Const aName : String; var aInfo : TResourceInfo) : Boolean;
  26. Procedure LoadHTMLLinkResources(const aURL : String; OnLoad : TResourcesLoadedEnumCallBack = Nil; OnError : TResourcesLoadErrorCallBack = Nil);
  27. implementation
  28. uses sysutils, js, web;
  29. var
  30. gMode: TResourceSource;
  31. { ---------------------------------------------------------------------
  32. Global entry points
  33. ---------------------------------------------------------------------}
  34. Function SeTResourceSource(aSource : TResourceSource) : TResourceSource;
  35. begin
  36. Result:=gMode;
  37. gMode:=aSource;
  38. end;
  39. Function GetResourceNames : TStringDynArray;
  40. begin
  41. Result:=GetResourceNames(gMode);
  42. end;
  43. Function EnumResources(aCallback : TResourceEnumCallBack) : Integer;
  44. begin
  45. Result:=EnumResources(gMode,aCallback);
  46. end;
  47. Function GetResourceInfo(Const aName : String; var aInfo : TResourceInfo) : Boolean;
  48. begin
  49. Result:=GetResourceInfo(gMode,aName,aInfo);
  50. end;
  51. { ---------------------------------------------------------------------
  52. JS resources
  53. ---------------------------------------------------------------------}
  54. Type
  55. TRTLResourceInfo = class external name 'Object' (TJSObject)
  56. name : string;
  57. encoding : string;
  58. resourceunit : string; external name 'unit';
  59. format : string;
  60. data : string;
  61. end;
  62. function rtlGetResourceList : TStringDynArray; external name 'rtl.getResourceList';
  63. function rtlGetResource(const aName : string) : TRTLResourceInfo; external name 'rtl.getResource';
  64. Function GetRTLResourceInfo(Const aName : String; var aInfo : TResourceInfo) : Boolean;
  65. Var
  66. RTLInfo : TRTLResourceInfo;
  67. begin
  68. RTLInfo:=rtlGetResource(lowercase(aName));
  69. Result:=Assigned(RTLInfo);
  70. if Result then
  71. begin
  72. aInfo.name:=RTLinfo.name;
  73. aInfo.encoding:=RTLinfo.encoding;
  74. aInfo.format:=RTLinfo.format;
  75. aInfo.resourceunit:=RTLinfo.resourceunit;
  76. aInfo.data:=RTLinfo.data;
  77. end;
  78. end;
  79. { ---------------------------------------------------------------------
  80. HTML resources
  81. ---------------------------------------------------------------------}
  82. Const
  83. IDPrefix = 'resource-';
  84. Function IsResourceLink(L : TJSHTMLLinkElement) : Boolean;
  85. begin
  86. Result:=(Copy(L.id,1,Length(IDPrefix))=IDPrefix) and (isDefined(L.Dataset['unit'])) and (Copy(L.href,1,4)='data')
  87. end;
  88. Function GetHTMLResources : TStringDynArray;
  89. Var
  90. LC : TJSHTMLCollection;
  91. L : TJSHTMLLinkElement;
  92. I : Integer;
  93. ID : String;
  94. begin
  95. SetLength(Result,0);
  96. if not isDefined(document) then // If called in Node...
  97. exit;
  98. // No cache, we do this dynamically: it's possible to add link nodes at runtime.
  99. LC:=document.getElementsByTagName('link');
  100. For I:=0 to LC.length-1 do
  101. begin
  102. L:=TJSHTMLLinkElement(LC[i]);
  103. ID:=L.ID;
  104. if IsResourceLink(L) then
  105. begin
  106. Delete(ID,1,Length(IDPrefix));
  107. if (ID<>'') then
  108. TJSArray(Result).Push(ID);
  109. end;
  110. end;
  111. end;
  112. Function GetHTMLResourceInfo(Const aName : String; var aInfo : TResourceInfo) : Boolean;
  113. Var
  114. el : TJSElement;
  115. L : TJSHTMLLinkElement absolute el;
  116. S : String;
  117. I : Integer;
  118. begin
  119. Result:=False;
  120. if not isDefined(document) then // If called in Node...
  121. exit;
  122. El:=document.getElementByID(IDPrefix+lowercase(aName));
  123. Result:=assigned(el) and SameText(el.tagName,'link');
  124. if not Result then
  125. exit;
  126. ainfo.name:=lowercase(aName);
  127. ainfo.Resourceunit:=String(L.Dataset['unit']);
  128. S:=L.href;
  129. S:=Copy(S,6,Length(S)-5); // strip data;
  130. I:=Pos(',',S);
  131. aInfo.data:=Copy(S,I+1,Length(S)-1);
  132. S:=copy(S,1,I-1);
  133. I:=Pos(';',S);
  134. if I=0 then
  135. aInfo.encoding:=''
  136. else
  137. begin
  138. aInfo.encoding:=Copy(S,I+1,Length(S)-1);
  139. S:=Copy(S,1,I-1);
  140. end;
  141. aInfo.Format:=S;
  142. end;
  143. Function HasTemplate : Boolean;
  144. begin
  145. asm
  146. return ('content' in document.createElement('template'))
  147. end;
  148. end;
  149. Procedure LoadHTMLLinkResources(const aURL : String; OnLoad : TResourcesLoadedEnumCallBack = Nil; OnError : TResourcesLoadErrorCallBack = Nil);
  150. function FetchOK(Res : JSValue) : JSValue;
  151. var
  152. Response : TJSResponse absolute res;
  153. begin
  154. Result:=Nil;
  155. if not Response.ok then
  156. begin
  157. if Assigned(OnError) then
  158. Raise TJSError.New('HTTP Error for URL aURL, status = '+IntToStr(Response.status)+' : '+Response.statusText)
  159. end
  160. else
  161. Result:=Response.Text();
  162. end;
  163. function BlobOK(Res : JSValue) : JSValue;
  164. Var
  165. aText : String absolute res;
  166. ID : String;
  167. Tmpl : TJSHTMLTemplateElement;
  168. El : TJSHTMLElement;
  169. L : TJSHTMLLinkElement absolute El;
  170. Arr : TStringDynArray;
  171. aParent : TJSHTMLElement;
  172. begin
  173. Result:=Nil;
  174. aParent:=TJSHTMLElement(document.head);
  175. if aParent=Nil then
  176. aParent:=TJSHTMLElement(document.body);
  177. SetLength(Arr,0);
  178. Tmpl:=TJSHTMLTemplateElement(Document.createElement('template'));
  179. Tmpl.innerhtml:=TJSString(aText).trim;
  180. el:=TJSHTMLElement(Tmpl.Content.firstElementChild);
  181. While El<>Nil do
  182. begin
  183. if SameText(El.tagName,'link') and IsResourceLink(L) then
  184. begin
  185. aParent.Append(TJSHTMLElement(document.importNode(l,true)));
  186. ID:=L.ID;
  187. Delete(ID,1,Length(IDPrefix));
  188. if (ID<>'') then
  189. TJSArray(Arr).Push(ID);
  190. end;
  191. el:=TJSHTMLElement(el.nextElementSibling);
  192. end;
  193. if Assigned(OnLoad) then
  194. OnLoad(Arr);
  195. end;
  196. function DoError (aValue : JSValue) : JSValue;
  197. Var
  198. aErr : TJSError absolute aValue;
  199. begin
  200. Result:=Nil;
  201. if Assigned(OnError) then
  202. if aErr=Nil then
  203. OnError('Error: ' + aErr.message)
  204. end;
  205. begin
  206. if not HasTemplate then
  207. begin
  208. if Assigned(OnError) then
  209. OnError('No template support in this browser')
  210. end
  211. else
  212. window.fetch(aURL)._then(@FetchOK)._then(@BlobOK).catch(@doError);
  213. end;
  214. { ---------------------------------------------------------------------
  215. Global entries, specifying resource mode
  216. ---------------------------------------------------------------------}
  217. Function GetResourceNames(aSource : TResourceSource) : TStringDynArray;
  218. begin
  219. case aSource of
  220. rsJS : Result:=rtlGetResourceList;
  221. rsHTML : Result:=GetHTMLResources;
  222. end;
  223. end;
  224. Function EnumResources(aSource : TResourceSource; aCallback : TResourceEnumCallBack) : Integer;
  225. Var
  226. RL : TStringDynArray;
  227. I : Integer;
  228. ContinueEnum : Boolean;
  229. begin
  230. Result:=0;
  231. RL:=GetResourceNames(aSource);
  232. I:=0;
  233. Result:=Length(RL);
  234. ContinueEnum:=True;
  235. While (I<Result) and ContinueEnum do
  236. begin
  237. ContinueEnum:=aCallBack(RL[i]);
  238. Inc(I);
  239. end;
  240. end;
  241. Function GetResourceInfo(aSource : TResourceSource; Const aName : String; var aInfo : TResourceInfo) : Boolean;
  242. begin
  243. case aSource of
  244. rsJS : Result:=GetRTLResourceInfo(aName,aInfo);
  245. rsHTML : Result:=GetHTMLResourceInfo(aName,aInfo);
  246. end;
  247. end;
  248. end.