datatable-debug.js 518 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361
  1. /*
  2. Copyright (c) 2009, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.net/yui/license.txt
  5. version: 2.8.0r4
  6. */
  7. /**
  8. * Mechanism to execute a series of callbacks in a non-blocking queue. Each callback is executed via setTimout unless configured with a negative timeout, in which case it is run in blocking mode in the same execution thread as the previous callback. Callbacks can be function references or object literals with the following keys:
  9. * <ul>
  10. * <li><code>method</code> - {Function} REQUIRED the callback function.</li>
  11. * <li><code>scope</code> - {Object} the scope from which to execute the callback. Default is the global window scope.</li>
  12. * <li><code>argument</code> - {Array} parameters to be passed to method as individual arguments.</li>
  13. * <li><code>timeout</code> - {number} millisecond delay to wait after previous callback completion before executing this callback. Negative values cause immediate blocking execution. Default 0.</li>
  14. * <li><code>until</code> - {Function} boolean function executed before each iteration. Return true to indicate completion and proceed to the next callback.</li>
  15. * <li><code>iterations</code> - {Number} number of times to execute the callback before proceeding to the next callback in the chain. Incompatible with <code>until</code>.</li>
  16. * </ul>
  17. *
  18. * @namespace YAHOO.util
  19. * @class Chain
  20. * @constructor
  21. * @param callback* {Function|Object} Any number of callbacks to initialize the queue
  22. */
  23. YAHOO.util.Chain = function () {
  24. /**
  25. * The callback queue
  26. * @property q
  27. * @type {Array}
  28. * @private
  29. */
  30. this.q = [].slice.call(arguments);
  31. /**
  32. * Event fired when the callback queue is emptied via execution (not via
  33. * a call to chain.stop().
  34. * @event end
  35. */
  36. this.createEvent('end');
  37. };
  38. YAHOO.util.Chain.prototype = {
  39. /**
  40. * Timeout id used to pause or stop execution and indicate the execution state of the Chain. 0 indicates paused or stopped, -1 indicates blocking execution, and any positive number indicates non-blocking execution.
  41. * @property id
  42. * @type {number}
  43. * @private
  44. */
  45. id : 0,
  46. /**
  47. * Begin executing the chain, or resume execution from the last paused position.
  48. * @method run
  49. * @return {Chain} the Chain instance
  50. */
  51. run : function () {
  52. // Grab the first callback in the queue
  53. var c = this.q[0],
  54. fn;
  55. // If there is no callback in the queue or the Chain is currently
  56. // in an execution mode, return
  57. if (!c) {
  58. this.fireEvent('end');
  59. return this;
  60. } else if (this.id) {
  61. return this;
  62. }
  63. fn = c.method || c;
  64. if (typeof fn === 'function') {
  65. var o = c.scope || {},
  66. args = c.argument || [],
  67. ms = c.timeout || 0,
  68. me = this;
  69. if (!(args instanceof Array)) {
  70. args = [args];
  71. }
  72. // Execute immediately if the callback timeout is negative.
  73. if (ms < 0) {
  74. this.id = ms;
  75. if (c.until) {
  76. for (;!c.until();) {
  77. // Execute the callback from scope, with argument
  78. fn.apply(o,args);
  79. }
  80. } else if (c.iterations) {
  81. for (;c.iterations-- > 0;) {
  82. fn.apply(o,args);
  83. }
  84. } else {
  85. fn.apply(o,args);
  86. }
  87. this.q.shift();
  88. this.id = 0;
  89. return this.run();
  90. } else {
  91. // If the until condition is set, check if we're done
  92. if (c.until) {
  93. if (c.until()) {
  94. // Shift this callback from the queue and execute the next
  95. // callback
  96. this.q.shift();
  97. return this.run();
  98. }
  99. // Otherwise if either iterations is not set or we're
  100. // executing the last iteration, shift callback from the queue
  101. } else if (!c.iterations || !--c.iterations) {
  102. this.q.shift();
  103. }
  104. // Otherwise set to execute after the configured timeout
  105. this.id = setTimeout(function () {
  106. // Execute the callback from scope, with argument
  107. fn.apply(o,args);
  108. // Check if the Chain was not paused from inside the callback
  109. if (me.id) {
  110. // Indicate ready to run state
  111. me.id = 0;
  112. // Start the fun all over again
  113. me.run();
  114. }
  115. },ms);
  116. }
  117. }
  118. return this;
  119. },
  120. /**
  121. * Add a callback to the end of the queue
  122. * @method add
  123. * @param c {Function|Object} the callback function ref or object literal
  124. * @return {Chain} the Chain instance
  125. */
  126. add : function (c) {
  127. this.q.push(c);
  128. return this;
  129. },
  130. /**
  131. * Pause the execution of the Chain after the current execution of the
  132. * current callback completes. If called interstitially, clears the
  133. * timeout for the pending callback. Paused Chains can be restarted with
  134. * chain.run()
  135. * @method pause
  136. * @return {Chain} the Chain instance
  137. */
  138. pause: function () {
  139. // Conditional added for Caja compatibility
  140. if (this.id > 0) {
  141. clearTimeout(this.id);
  142. }
  143. this.id = 0;
  144. return this;
  145. },
  146. /**
  147. * Stop and clear the Chain's queue after the current execution of the
  148. * current callback completes.
  149. * @method stop
  150. * @return {Chain} the Chain instance
  151. */
  152. stop : function () {
  153. this.pause();
  154. this.q = [];
  155. return this;
  156. }
  157. };
  158. YAHOO.lang.augmentProto(YAHOO.util.Chain,YAHOO.util.EventProvider);
  159. /****************************************************************************/
  160. /****************************************************************************/
  161. /****************************************************************************/
  162. /**
  163. * The ColumnSet class defines and manages a DataTable's Columns,
  164. * including nested hierarchies and access to individual Column instances.
  165. *
  166. * @namespace YAHOO.widget
  167. * @class ColumnSet
  168. * @uses YAHOO.util.EventProvider
  169. * @constructor
  170. * @param aDefinitions {Object[]} Array of object literals that define cells in
  171. * the THEAD.
  172. */
  173. YAHOO.widget.ColumnSet = function(aDefinitions) {
  174. this._sId = "yui-cs" + YAHOO.widget.ColumnSet._nCount;
  175. // First clone the defs
  176. aDefinitions = YAHOO.widget.DataTable._cloneObject(aDefinitions);
  177. this._init(aDefinitions);
  178. YAHOO.widget.ColumnSet._nCount++;
  179. YAHOO.log("ColumnSet initialized", "info", this.toString());
  180. };
  181. /////////////////////////////////////////////////////////////////////////////
  182. //
  183. // Private member variables
  184. //
  185. /////////////////////////////////////////////////////////////////////////////
  186. /**
  187. * Internal class variable to index multiple ColumnSet instances.
  188. *
  189. * @property ColumnSet._nCount
  190. * @type Number
  191. * @private
  192. * @static
  193. */
  194. YAHOO.widget.ColumnSet._nCount = 0;
  195. YAHOO.widget.ColumnSet.prototype = {
  196. /**
  197. * Unique instance name.
  198. *
  199. * @property _sId
  200. * @type String
  201. * @private
  202. */
  203. _sId : null,
  204. /**
  205. * Array of object literal Column definitions passed to the constructor.
  206. *
  207. * @property _aDefinitions
  208. * @type Object[]
  209. * @private
  210. */
  211. _aDefinitions : null,
  212. /////////////////////////////////////////////////////////////////////////////
  213. //
  214. // Public member variables
  215. //
  216. /////////////////////////////////////////////////////////////////////////////
  217. /**
  218. * Top-down tree representation of Column hierarchy.
  219. *
  220. * @property tree
  221. * @type YAHOO.widget.Column[]
  222. */
  223. tree : null,
  224. /**
  225. * Flattened representation of all Columns.
  226. *
  227. * @property flat
  228. * @type YAHOO.widget.Column[]
  229. * @default []
  230. */
  231. flat : null,
  232. /**
  233. * Array of Columns that map one-to-one to a table column.
  234. *
  235. * @property keys
  236. * @type YAHOO.widget.Column[]
  237. * @default []
  238. */
  239. keys : null,
  240. /**
  241. * ID index of nested parent hierarchies for HEADERS accessibility attribute.
  242. *
  243. * @property headers
  244. * @type String[]
  245. * @default []
  246. */
  247. headers : null,
  248. /////////////////////////////////////////////////////////////////////////////
  249. //
  250. // Private methods
  251. //
  252. /////////////////////////////////////////////////////////////////////////////
  253. /**
  254. * Initializes ColumnSet instance with data from Column definitions.
  255. *
  256. * @method _init
  257. * @param aDefinitions {Object[]} Array of object literals that define cells in
  258. * the THEAD .
  259. * @private
  260. */
  261. _init : function(aDefinitions) {
  262. // DOM tree representation of all Columns
  263. var tree = [];
  264. // Flat representation of all Columns
  265. var flat = [];
  266. // Flat representation of only Columns that are meant to display data
  267. var keys = [];
  268. // Array of HEADERS attribute values for all keys in the "keys" array
  269. var headers = [];
  270. // Tracks current node list depth being tracked
  271. var nodeDepth = -1;
  272. // Internal recursive function to define Column instances
  273. var parseColumns = function(nodeList, parent) {
  274. // One level down
  275. nodeDepth++;
  276. // Create corresponding tree node if not already there for this depth
  277. if(!tree[nodeDepth]) {
  278. tree[nodeDepth] = [];
  279. }
  280. // Parse each node at this depth for attributes and any children
  281. for(var j=0; j<nodeList.length; j++) {
  282. var currentNode = nodeList[j];
  283. // Instantiate a new Column for each node
  284. var oColumn = new YAHOO.widget.Column(currentNode);
  285. // Cross-reference Column ID back to the original object literal definition
  286. currentNode.yuiColumnId = oColumn._sId;
  287. // Add the new Column to the flat list
  288. flat.push(oColumn);
  289. // Assign its parent as an attribute, if applicable
  290. if(parent) {
  291. oColumn._oParent = parent;
  292. }
  293. // The Column has descendants
  294. if(YAHOO.lang.isArray(currentNode.children)) {
  295. oColumn.children = currentNode.children;
  296. // Determine COLSPAN value for this Column
  297. var terminalChildNodes = 0;
  298. var countTerminalChildNodes = function(ancestor) {
  299. var descendants = ancestor.children;
  300. // Drill down each branch and count terminal nodes
  301. for(var k=0; k<descendants.length; k++) {
  302. // Keep drilling down
  303. if(YAHOO.lang.isArray(descendants[k].children)) {
  304. countTerminalChildNodes(descendants[k]);
  305. }
  306. // Reached branch terminus
  307. else {
  308. terminalChildNodes++;
  309. }
  310. }
  311. };
  312. countTerminalChildNodes(currentNode);
  313. oColumn._nColspan = terminalChildNodes;
  314. // Cascade certain properties to children if not defined on their own
  315. var currentChildren = currentNode.children;
  316. for(var k=0; k<currentChildren.length; k++) {
  317. var child = currentChildren[k];
  318. if(oColumn.className && (child.className === undefined)) {
  319. child.className = oColumn.className;
  320. }
  321. if(oColumn.editor && (child.editor === undefined)) {
  322. child.editor = oColumn.editor;
  323. }
  324. //TODO: Deprecated
  325. if(oColumn.editorOptions && (child.editorOptions === undefined)) {
  326. child.editorOptions = oColumn.editorOptions;
  327. }
  328. if(oColumn.formatter && (child.formatter === undefined)) {
  329. child.formatter = oColumn.formatter;
  330. }
  331. if(oColumn.resizeable && (child.resizeable === undefined)) {
  332. child.resizeable = oColumn.resizeable;
  333. }
  334. if(oColumn.sortable && (child.sortable === undefined)) {
  335. child.sortable = oColumn.sortable;
  336. }
  337. if(oColumn.hidden) {
  338. child.hidden = true;
  339. }
  340. if(oColumn.width && (child.width === undefined)) {
  341. child.width = oColumn.width;
  342. }
  343. if(oColumn.minWidth && (child.minWidth === undefined)) {
  344. child.minWidth = oColumn.minWidth;
  345. }
  346. if(oColumn.maxAutoWidth && (child.maxAutoWidth === undefined)) {
  347. child.maxAutoWidth = oColumn.maxAutoWidth;
  348. }
  349. // Backward compatibility
  350. if(oColumn.type && (child.type === undefined)) {
  351. child.type = oColumn.type;
  352. }
  353. if(oColumn.type && !oColumn.formatter) {
  354. YAHOO.log("The property type has been" +
  355. " deprecated in favor of formatter", "warn", oColumn.toString());
  356. oColumn.formatter = oColumn.type;
  357. }
  358. if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) {
  359. YAHOO.log("The property text has been" +
  360. " deprecated in favor of label", "warn", oColumn.toString());
  361. oColumn.label = oColumn.text;
  362. }
  363. if(oColumn.parser) {
  364. YAHOO.log("The property parser is no longer supported",
  365. "warn", this.toString());
  366. }
  367. if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) ||
  368. (oColumn.sortOptions.descFunction))) {
  369. YAHOO.log("The properties sortOptions.ascFunction and " +
  370. " sortOptions.descFunction have been deprecated in favor " +
  371. " of sortOptions.sortFunction", "warn", oColumn.toString());
  372. }
  373. }
  374. // The children themselves must also be parsed for Column instances
  375. if(!tree[nodeDepth+1]) {
  376. tree[nodeDepth+1] = [];
  377. }
  378. parseColumns(currentChildren, oColumn);
  379. }
  380. // This Column does not have any children
  381. else {
  382. oColumn._nKeyIndex = keys.length;
  383. oColumn._nColspan = 1;
  384. keys.push(oColumn);
  385. }
  386. // Add the Column to the top-down tree
  387. tree[nodeDepth].push(oColumn);
  388. }
  389. nodeDepth--;
  390. };
  391. // Parse out Column instances from the array of object literals
  392. if(YAHOO.lang.isArray(aDefinitions)) {
  393. parseColumns(aDefinitions);
  394. // Store the array
  395. this._aDefinitions = aDefinitions;
  396. }
  397. else {
  398. YAHOO.log("Could not initialize ColumnSet due to invalid definitions","error");
  399. return null;
  400. }
  401. var i;
  402. // Determine ROWSPAN value for each Column in the tree
  403. var parseTreeForRowspan = function(tree) {
  404. var maxRowDepth = 1;
  405. var currentRow;
  406. var currentColumn;
  407. // Calculate the max depth of descendants for this row
  408. var countMaxRowDepth = function(row, tmpRowDepth) {
  409. tmpRowDepth = tmpRowDepth || 1;
  410. for(var n=0; n<row.length; n++) {
  411. var col = row[n];
  412. // Column has children, so keep counting
  413. if(YAHOO.lang.isArray(col.children)) {
  414. tmpRowDepth++;
  415. countMaxRowDepth(col.children, tmpRowDepth);
  416. tmpRowDepth--;
  417. }
  418. // No children, is it the max depth?
  419. else {
  420. if(tmpRowDepth > maxRowDepth) {
  421. maxRowDepth = tmpRowDepth;
  422. }
  423. }
  424. }
  425. };
  426. // Count max row depth for each row
  427. for(var m=0; m<tree.length; m++) {
  428. currentRow = tree[m];
  429. countMaxRowDepth(currentRow);
  430. // Assign the right ROWSPAN values to each Column in the row
  431. for(var p=0; p<currentRow.length; p++) {
  432. currentColumn = currentRow[p];
  433. if(!YAHOO.lang.isArray(currentColumn.children)) {
  434. currentColumn._nRowspan = maxRowDepth;
  435. }
  436. else {
  437. currentColumn._nRowspan = 1;
  438. }
  439. }
  440. // Reset counter for next row
  441. maxRowDepth = 1;
  442. }
  443. };
  444. parseTreeForRowspan(tree);
  445. // Store tree index values
  446. for(i=0; i<tree[0].length; i++) {
  447. tree[0][i]._nTreeIndex = i;
  448. }
  449. // Store header relationships in an array for HEADERS attribute
  450. var recurseAncestorsForHeaders = function(i, oColumn) {
  451. headers[i].push(oColumn.getSanitizedKey());
  452. if(oColumn._oParent) {
  453. recurseAncestorsForHeaders(i, oColumn._oParent);
  454. }
  455. };
  456. for(i=0; i<keys.length; i++) {
  457. headers[i] = [];
  458. recurseAncestorsForHeaders(i, keys[i]);
  459. headers[i] = headers[i].reverse();
  460. }
  461. // Save to the ColumnSet instance
  462. this.tree = tree;
  463. this.flat = flat;
  464. this.keys = keys;
  465. this.headers = headers;
  466. },
  467. /////////////////////////////////////////////////////////////////////////////
  468. //
  469. // Public methods
  470. //
  471. /////////////////////////////////////////////////////////////////////////////
  472. /**
  473. * Returns unique name of the ColumnSet instance.
  474. *
  475. * @method getId
  476. * @return {String} Unique name of the ColumnSet instance.
  477. */
  478. getId : function() {
  479. return this._sId;
  480. },
  481. /**
  482. * ColumnSet instance name, for logging.
  483. *
  484. * @method toString
  485. * @return {String} Unique name of the ColumnSet instance.
  486. */
  487. toString : function() {
  488. return "ColumnSet instance " + this._sId;
  489. },
  490. /**
  491. * Public accessor to the definitions array.
  492. *
  493. * @method getDefinitions
  494. * @return {Object[]} Array of object literal Column definitions.
  495. */
  496. getDefinitions : function() {
  497. var aDefinitions = this._aDefinitions;
  498. // Internal recursive function to define Column instances
  499. var parseColumns = function(nodeList, oSelf) {
  500. // Parse each node at this depth for attributes and any children
  501. for(var j=0; j<nodeList.length; j++) {
  502. var currentNode = nodeList[j];
  503. // Get the Column for each node
  504. var oColumn = oSelf.getColumnById(currentNode.yuiColumnId);
  505. if(oColumn) {
  506. // Update the current values
  507. var oDefinition = oColumn.getDefinition();
  508. for(var name in oDefinition) {
  509. if(YAHOO.lang.hasOwnProperty(oDefinition, name)) {
  510. currentNode[name] = oDefinition[name];
  511. }
  512. }
  513. }
  514. // The Column has descendants
  515. if(YAHOO.lang.isArray(currentNode.children)) {
  516. // The children themselves must also be parsed for Column instances
  517. parseColumns(currentNode.children, oSelf);
  518. }
  519. }
  520. };
  521. parseColumns(aDefinitions, this);
  522. this._aDefinitions = aDefinitions;
  523. return aDefinitions;
  524. },
  525. /**
  526. * Returns Column instance with given ID.
  527. *
  528. * @method getColumnById
  529. * @param column {String} Column ID.
  530. * @return {YAHOO.widget.Column} Column instance.
  531. */
  532. getColumnById : function(column) {
  533. if(YAHOO.lang.isString(column)) {
  534. var allColumns = this.flat;
  535. for(var i=allColumns.length-1; i>-1; i--) {
  536. if(allColumns[i]._sId === column) {
  537. return allColumns[i];
  538. }
  539. }
  540. }
  541. return null;
  542. },
  543. /**
  544. * Returns Column instance with given key or ColumnSet key index.
  545. *
  546. * @method getColumn
  547. * @param column {String | Number} Column key or ColumnSet key index.
  548. * @return {YAHOO.widget.Column} Column instance.
  549. */
  550. getColumn : function(column) {
  551. if(YAHOO.lang.isNumber(column) && this.keys[column]) {
  552. return this.keys[column];
  553. }
  554. else if(YAHOO.lang.isString(column)) {
  555. var allColumns = this.flat;
  556. var aColumns = [];
  557. for(var i=0; i<allColumns.length; i++) {
  558. if(allColumns[i].key === column) {
  559. aColumns.push(allColumns[i]);
  560. }
  561. }
  562. if(aColumns.length === 1) {
  563. return aColumns[0];
  564. }
  565. else if(aColumns.length > 1) {
  566. return aColumns;
  567. }
  568. }
  569. return null;
  570. },
  571. /**
  572. * Public accessor returns array of given Column's desendants (if any), including itself.
  573. *
  574. * @method getDescendants
  575. * @parem {YAHOO.widget.Column} Column instance.
  576. * @return {Array} Array including the Column itself and all descendants (if any).
  577. */
  578. getDescendants : function(oColumn) {
  579. var oSelf = this;
  580. var allDescendants = [];
  581. var i;
  582. // Recursive function to loop thru all children
  583. var parse = function(oParent) {
  584. allDescendants.push(oParent);
  585. // This Column has children
  586. if(oParent.children) {
  587. for(i=0; i<oParent.children.length; i++) {
  588. parse(oSelf.getColumn(oParent.children[i].key));
  589. }
  590. }
  591. };
  592. parse(oColumn);
  593. return allDescendants;
  594. }
  595. };
  596. /****************************************************************************/
  597. /****************************************************************************/
  598. /****************************************************************************/
  599. /**
  600. * The Column class defines and manages attributes of DataTable Columns
  601. *
  602. * @namespace YAHOO.widget
  603. * @class Column
  604. * @constructor
  605. * @param oConfigs {Object} Object literal of definitions.
  606. */
  607. YAHOO.widget.Column = function(oConfigs) {
  608. this._sId = "yui-col" + YAHOO.widget.Column._nCount;
  609. // Object literal defines Column attributes
  610. if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
  611. for(var sConfig in oConfigs) {
  612. if(sConfig) {
  613. this[sConfig] = oConfigs[sConfig];
  614. }
  615. }
  616. }
  617. // Assign a key if not found
  618. if(!YAHOO.lang.isValue(this.key)) {
  619. this.key = "yui-dt-col" + YAHOO.widget.Column._nCount;
  620. }
  621. // Assign a field if not found, defaults to key
  622. if(!YAHOO.lang.isValue(this.field)) {
  623. this.field = this.key;
  624. }
  625. // Increment counter
  626. YAHOO.widget.Column._nCount++;
  627. // Backward compatibility
  628. if(this.width && !YAHOO.lang.isNumber(this.width)) {
  629. this.width = null;
  630. YAHOO.log("The Column property width must be a number", "warn", this.toString());
  631. }
  632. if(this.editor && YAHOO.lang.isString(this.editor)) {
  633. this.editor = new YAHOO.widget.CellEditor(this.editor, this.editorOptions);
  634. YAHOO.log("The Column property editor must be an instance of YAHOO.widget.CellEditor", "warn", this.toString());
  635. }
  636. };
  637. /////////////////////////////////////////////////////////////////////////////
  638. //
  639. // Private member variables
  640. //
  641. /////////////////////////////////////////////////////////////////////////////
  642. YAHOO.lang.augmentObject(YAHOO.widget.Column, {
  643. /**
  644. * Internal class variable to index multiple Column instances.
  645. *
  646. * @property Column._nCount
  647. * @type Number
  648. * @private
  649. * @static
  650. */
  651. _nCount : 0,
  652. formatCheckbox : function(elCell, oRecord, oColumn, oData) {
  653. YAHOO.log("The method YAHOO.widget.Column.formatCheckbox() has been" +
  654. " deprecated in favor of YAHOO.widget.DataTable.formatCheckbox()", "warn",
  655. "YAHOO.widget.Column.formatCheckbox");
  656. YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData);
  657. },
  658. formatCurrency : function(elCell, oRecord, oColumn, oData) {
  659. YAHOO.log("The method YAHOO.widget.Column.formatCurrency() has been" +
  660. " deprecated in favor of YAHOO.widget.DataTable.formatCurrency()", "warn",
  661. "YAHOO.widget.Column.formatCurrency");
  662. YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData);
  663. },
  664. formatDate : function(elCell, oRecord, oColumn, oData) {
  665. YAHOO.log("The method YAHOO.widget.Column.formatDate() has been" +
  666. " deprecated in favor of YAHOO.widget.DataTable.formatDate()", "warn",
  667. "YAHOO.widget.Column.formatDate");
  668. YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData);
  669. },
  670. formatEmail : function(elCell, oRecord, oColumn, oData) {
  671. YAHOO.log("The method YAHOO.widget.Column.formatEmail() has been" +
  672. " deprecated in favor of YAHOO.widget.DataTable.formatEmail()", "warn",
  673. "YAHOO.widget.Column.formatEmail");
  674. YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData);
  675. },
  676. formatLink : function(elCell, oRecord, oColumn, oData) {
  677. YAHOO.log("The method YAHOO.widget.Column.formatLink() has been" +
  678. " deprecated in favor of YAHOO.widget.DataTable.formatLink()", "warn",
  679. "YAHOO.widget.Column.formatLink");
  680. YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData);
  681. },
  682. formatNumber : function(elCell, oRecord, oColumn, oData) {
  683. YAHOO.log("The method YAHOO.widget.Column.formatNumber() has been" +
  684. " deprecated in favor of YAHOO.widget.DataTable.formatNumber()", "warn",
  685. "YAHOO.widget.Column.formatNumber");
  686. YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData);
  687. },
  688. formatSelect : function(elCell, oRecord, oColumn, oData) {
  689. YAHOO.log("The method YAHOO.widget.Column.formatSelect() has been" +
  690. " deprecated in favor of YAHOO.widget.DataTable.formatDropdown()", "warn",
  691. "YAHOO.widget.Column.formatSelect");
  692. YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData);
  693. }
  694. });
  695. YAHOO.widget.Column.prototype = {
  696. /**
  697. * Unique String identifier assigned at instantiation.
  698. *
  699. * @property _sId
  700. * @type String
  701. * @private
  702. */
  703. _sId : null,
  704. /**
  705. * Reference to Column's current position index within its ColumnSet's keys
  706. * array, if applicable. This property only applies to non-nested and bottom-
  707. * level child Columns.
  708. *
  709. * @property _nKeyIndex
  710. * @type Number
  711. * @private
  712. */
  713. _nKeyIndex : null,
  714. /**
  715. * Reference to Column's current position index within its ColumnSet's tree
  716. * array, if applicable. This property only applies to non-nested and top-
  717. * level parent Columns.
  718. *
  719. * @property _nTreeIndex
  720. * @type Number
  721. * @private
  722. */
  723. _nTreeIndex : null,
  724. /**
  725. * Number of table cells the Column spans.
  726. *
  727. * @property _nColspan
  728. * @type Number
  729. * @private
  730. */
  731. _nColspan : 1,
  732. /**
  733. * Number of table rows the Column spans.
  734. *
  735. * @property _nRowspan
  736. * @type Number
  737. * @private
  738. */
  739. _nRowspan : 1,
  740. /**
  741. * Column's parent Column instance, or null.
  742. *
  743. * @property _oParent
  744. * @type YAHOO.widget.Column
  745. * @private
  746. */
  747. _oParent : null,
  748. /**
  749. * The DOM reference to the associated TH element.
  750. *
  751. * @property _elTh
  752. * @type HTMLElement
  753. * @private
  754. */
  755. _elTh : null,
  756. /**
  757. * The DOM reference to the associated TH element's liner DIV element.
  758. *
  759. * @property _elThLiner
  760. * @type HTMLElement
  761. * @private
  762. */
  763. _elThLiner : null,
  764. /**
  765. * The DOM reference to the associated TH element's label SPAN element.
  766. *
  767. * @property _elThLabel
  768. * @type HTMLElement
  769. * @private
  770. */
  771. _elThLabel : null,
  772. /**
  773. * The DOM reference to the associated resizerelement (if any).
  774. *
  775. * @property _elResizer
  776. * @type HTMLElement
  777. * @private
  778. */
  779. _elResizer : null,
  780. /**
  781. * Internal width tracker.
  782. *
  783. * @property _nWidth
  784. * @type Number
  785. * @private
  786. */
  787. _nWidth : null,
  788. /**
  789. * For unreg() purposes, a reference to the Column's DragDrop instance.
  790. *
  791. * @property _dd
  792. * @type YAHOO.util.DragDrop
  793. * @private
  794. */
  795. _dd : null,
  796. /**
  797. * For unreg() purposes, a reference to the Column resizer's DragDrop instance.
  798. *
  799. * @property _ddResizer
  800. * @type YAHOO.util.DragDrop
  801. * @private
  802. */
  803. _ddResizer : null,
  804. /////////////////////////////////////////////////////////////////////////////
  805. //
  806. // Public member variables
  807. //
  808. /////////////////////////////////////////////////////////////////////////////
  809. /**
  810. * Unique name, required.
  811. *
  812. * @property key
  813. * @type String
  814. */
  815. key : null,
  816. /**
  817. * Associated database field, or null.
  818. *
  819. * @property field
  820. * @type String
  821. */
  822. field : null,
  823. /**
  824. * Text or HTML for display as Column's label in the TH element.
  825. *
  826. * @property label
  827. * @type String
  828. */
  829. label : null,
  830. /**
  831. * Column head cell ABBR for accessibility.
  832. *
  833. * @property abbr
  834. * @type String
  835. */
  836. abbr : null,
  837. /**
  838. * Array of object literals that define children (nested headers) of a Column.
  839. *
  840. * @property children
  841. * @type Object[]
  842. */
  843. children : null,
  844. /**
  845. * Column width (in pixels).
  846. *
  847. * @property width
  848. * @type Number
  849. */
  850. width : null,
  851. /**
  852. * Minimum Column width (in pixels).
  853. *
  854. * @property minWidth
  855. * @type Number
  856. * @default null
  857. */
  858. minWidth : null,
  859. /**
  860. * When a width is not defined for a Column, maxAutoWidth defines an upper
  861. * limit that the Column should be auto-sized to. If resizeable is enabled,
  862. * users may still resize to a greater width. Most useful for Columns intended
  863. * to hold long unbroken, unwrapped Strings, such as URLs, to prevent very
  864. * wide Columns from disrupting visual readability by inducing truncation.
  865. *
  866. * @property maxAutoWidth
  867. * @type Number
  868. * @default null
  869. */
  870. maxAutoWidth : null,
  871. /**
  872. * True if Column is in hidden state.
  873. *
  874. * @property hidden
  875. * @type Boolean
  876. * @default false
  877. */
  878. hidden : false,
  879. /**
  880. * True if Column is in selected state.
  881. *
  882. * @property selected
  883. * @type Boolean
  884. * @default false
  885. */
  886. selected : false,
  887. /**
  888. * Custom CSS class or array of classes to be applied to every cell in the Column.
  889. *
  890. * @property className
  891. * @type String || String[]
  892. */
  893. className : null,
  894. /**
  895. * Defines a format function.
  896. *
  897. * @property formatter
  898. * @type String || HTMLFunction
  899. */
  900. formatter : null,
  901. /**
  902. * Config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
  903. *
  904. * @property currencyOptions
  905. * @type Object
  906. * @default null
  907. */
  908. currencyOptions : null,
  909. /**
  910. * Config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
  911. *
  912. * @property dateOptions
  913. * @type Object
  914. * @default null
  915. */
  916. dateOptions : null,
  917. /**
  918. * Array of dropdown values for formatter:"dropdown" cases. Can either be a simple array (e.g.,
  919. * ["Alabama","Alaska","Arizona","Arkansas"]) or a an array of objects (e.g.,
  920. * [{label:"Alabama", value:"AL"}, {label:"Alaska", value:"AK"},
  921. * {label:"Arizona", value:"AZ"}, {label:"Arkansas", value:"AR"}]).
  922. *
  923. * @property dropdownOptions
  924. * @type String[] | Object[]
  925. */
  926. dropdownOptions : null,
  927. /**
  928. * A CellEditor instance, otherwise Column is not editable.
  929. *
  930. * @property editor
  931. * @type YAHOO.widget.CellEditor
  932. */
  933. editor : null,
  934. /**
  935. * True if Column is resizeable, false otherwise. The Drag & Drop Utility is
  936. * required to enable this feature. Only bottom-level and non-nested Columns are
  937. * resizeble.
  938. *
  939. * @property resizeable
  940. * @type Boolean
  941. * @default false
  942. */
  943. resizeable : false,
  944. /**
  945. * True if Column is sortable, false otherwise.
  946. *
  947. * @property sortable
  948. * @type Boolean
  949. * @default false
  950. */
  951. sortable : false,
  952. /**
  953. * @property sortOptions.defaultOrder
  954. * @deprecated Use sortOptions.defaultDir.
  955. */
  956. /**
  957. * Default sort direction for Column: YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC.
  958. *
  959. * @property sortOptions.defaultDir
  960. * @type String
  961. * @default null
  962. */
  963. /**
  964. * Custom field to sort on.
  965. *
  966. * @property sortOptions.field
  967. * @type String
  968. * @default null
  969. */
  970. /**
  971. * Custom sort handler. Signature: sortFunction(a, b, desc, field) where field is the sortOptions.field value
  972. *
  973. * @property sortOptions.sortFunction
  974. * @type Function
  975. * @default null
  976. */
  977. sortOptions : null,
  978. /////////////////////////////////////////////////////////////////////////////
  979. //
  980. // Public methods
  981. //
  982. /////////////////////////////////////////////////////////////////////////////
  983. /**
  984. * Returns unique ID string.
  985. *
  986. * @method getId
  987. * @return {String} Unique ID string.
  988. */
  989. getId : function() {
  990. return this._sId;
  991. },
  992. /**
  993. * Column instance name, for logging.
  994. *
  995. * @method toString
  996. * @return {String} Column's unique name.
  997. */
  998. toString : function() {
  999. return "Column instance " + this._sId;
  1000. },
  1001. /**
  1002. * Returns object literal definition.
  1003. *
  1004. * @method getDefinition
  1005. * @return {Object} Object literal definition.
  1006. */
  1007. getDefinition : function() {
  1008. var oDefinition = {};
  1009. // Update the definition
  1010. oDefinition.abbr = this.abbr;
  1011. oDefinition.className = this.className;
  1012. oDefinition.editor = this.editor;
  1013. oDefinition.editorOptions = this.editorOptions; //TODO: deprecated
  1014. oDefinition.field = this.field;
  1015. oDefinition.formatter = this.formatter;
  1016. oDefinition.hidden = this.hidden;
  1017. oDefinition.key = this.key;
  1018. oDefinition.label = this.label;
  1019. oDefinition.minWidth = this.minWidth;
  1020. oDefinition.maxAutoWidth = this.maxAutoWidth;
  1021. oDefinition.resizeable = this.resizeable;
  1022. oDefinition.selected = this.selected;
  1023. oDefinition.sortable = this.sortable;
  1024. oDefinition.sortOptions = this.sortOptions;
  1025. oDefinition.width = this.width;
  1026. return oDefinition;
  1027. },
  1028. /**
  1029. * Returns unique Column key.
  1030. *
  1031. * @method getKey
  1032. * @return {String} Column key.
  1033. */
  1034. getKey : function() {
  1035. return this.key;
  1036. },
  1037. /**
  1038. * Returns field.
  1039. *
  1040. * @method getField
  1041. * @return {String} Column field.
  1042. */
  1043. getField : function() {
  1044. return this.field;
  1045. },
  1046. /**
  1047. * Returns Column key which has been sanitized for DOM (class and ID) usage
  1048. * starts with letter, contains only letters, numbers, hyphen, or period.
  1049. *
  1050. * @method getSanitizedKey
  1051. * @return {String} Sanitized Column key.
  1052. */
  1053. getSanitizedKey : function() {
  1054. return this.getKey().replace(/[^\w\-]/g,"");
  1055. },
  1056. /**
  1057. * Public accessor returns Column's current position index within its
  1058. * ColumnSet's keys array, if applicable. Only non-nested and bottom-level
  1059. * child Columns will return a value.
  1060. *
  1061. * @method getKeyIndex
  1062. * @return {Number} Position index, or null.
  1063. */
  1064. getKeyIndex : function() {
  1065. return this._nKeyIndex;
  1066. },
  1067. /**
  1068. * Public accessor returns Column's current position index within its
  1069. * ColumnSet's tree array, if applicable. Only non-nested and top-level parent
  1070. * Columns will return a value;
  1071. *
  1072. * @method getTreeIndex
  1073. * @return {Number} Position index, or null.
  1074. */
  1075. getTreeIndex : function() {
  1076. return this._nTreeIndex;
  1077. },
  1078. /**
  1079. * Public accessor returns Column's parent instance if any, or null otherwise.
  1080. *
  1081. * @method getParent
  1082. * @return {YAHOO.widget.Column} Column's parent instance.
  1083. */
  1084. getParent : function() {
  1085. return this._oParent;
  1086. },
  1087. /**
  1088. * Public accessor returns Column's calculated COLSPAN value.
  1089. *
  1090. * @method getColspan
  1091. * @return {Number} Column's COLSPAN value.
  1092. */
  1093. getColspan : function() {
  1094. return this._nColspan;
  1095. },
  1096. // Backward compatibility
  1097. getColSpan : function() {
  1098. YAHOO.log("The method getColSpan() has been" +
  1099. " deprecated in favor of getColspan()", "warn", this.toString());
  1100. return this.getColspan();
  1101. },
  1102. /**
  1103. * Public accessor returns Column's calculated ROWSPAN value.
  1104. *
  1105. * @method getRowspan
  1106. * @return {Number} Column's ROWSPAN value.
  1107. */
  1108. getRowspan : function() {
  1109. return this._nRowspan;
  1110. },
  1111. /**
  1112. * Returns DOM reference to the key TH element.
  1113. *
  1114. * @method getThEl
  1115. * @return {HTMLElement} TH element.
  1116. */
  1117. getThEl : function() {
  1118. return this._elTh;
  1119. },
  1120. /**
  1121. * Returns DOM reference to the TH's liner DIV element. Introduced since
  1122. * resizeable Columns may have an extra resizer liner, making the DIV liner
  1123. * not reliably the TH element's first child.
  1124. *
  1125. * @method getThLInerEl
  1126. * @return {HTMLElement} TH element.
  1127. */
  1128. getThLinerEl : function() {
  1129. return this._elThLiner;
  1130. },
  1131. /**
  1132. * Returns DOM reference to the resizer element, or null.
  1133. *
  1134. * @method getResizerEl
  1135. * @return {HTMLElement} DIV element.
  1136. */
  1137. getResizerEl : function() {
  1138. return this._elResizer;
  1139. },
  1140. // Backward compatibility
  1141. /**
  1142. * @method getColEl
  1143. * @deprecated Use getThEl
  1144. */
  1145. getColEl : function() {
  1146. YAHOO.log("The method getColEl() has been" +
  1147. " deprecated in favor of getThEl()", "warn",
  1148. this.toString());
  1149. return this.getThEl();
  1150. },
  1151. getIndex : function() {
  1152. YAHOO.log("The method getIndex() has been" +
  1153. " deprecated in favor of getKeyIndex()", "warn",
  1154. this.toString());
  1155. return this.getKeyIndex();
  1156. },
  1157. format : function() {
  1158. YAHOO.log("The method format() has been deprecated in favor of the " +
  1159. "DataTable method formatCell()", "error", this.toString());
  1160. }
  1161. };
  1162. /****************************************************************************/
  1163. /****************************************************************************/
  1164. /****************************************************************************/
  1165. /**
  1166. * Sort static utility to support Column sorting.
  1167. *
  1168. * @namespace YAHOO.util
  1169. * @class Sort
  1170. * @static
  1171. */
  1172. YAHOO.util.Sort = {
  1173. /////////////////////////////////////////////////////////////////////////////
  1174. //
  1175. // Public methods
  1176. //
  1177. /////////////////////////////////////////////////////////////////////////////
  1178. /**
  1179. * Comparator function for simple case-insensitive string sorting.
  1180. *
  1181. * @method compare
  1182. * @param a {Object} First sort argument.
  1183. * @param b {Object} Second sort argument.
  1184. * @param desc {Boolean} True if sort direction is descending, false if
  1185. * sort direction is ascending.
  1186. */
  1187. compare: function(a, b, desc) {
  1188. if((a === null) || (typeof a == "undefined")) {
  1189. if((b === null) || (typeof b == "undefined")) {
  1190. return 0;
  1191. }
  1192. else {
  1193. return 1;
  1194. }
  1195. }
  1196. else if((b === null) || (typeof b == "undefined")) {
  1197. return -1;
  1198. }
  1199. if(a.constructor == String) {
  1200. a = a.toLowerCase();
  1201. }
  1202. if(b.constructor == String) {
  1203. b = b.toLowerCase();
  1204. }
  1205. if(a < b) {
  1206. return (desc) ? 1 : -1;
  1207. }
  1208. else if (a > b) {
  1209. return (desc) ? -1 : 1;
  1210. }
  1211. else {
  1212. return 0;
  1213. }
  1214. }
  1215. };
  1216. /****************************************************************************/
  1217. /****************************************************************************/
  1218. /****************************************************************************/
  1219. /**
  1220. * ColumnDD subclasses DragDrop to support rearrangeable Columns.
  1221. *
  1222. * @namespace YAHOO.util
  1223. * @class ColumnDD
  1224. * @extends YAHOO.util.DDProxy
  1225. * @constructor
  1226. * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
  1227. * @param oColumn {YAHOO.widget.Column} Column instance.
  1228. * @param elTh {HTMLElement} TH element reference.
  1229. * @param elTarget {HTMLElement} Drag target element.
  1230. */
  1231. YAHOO.widget.ColumnDD = function(oDataTable, oColumn, elTh, elTarget) {
  1232. if(oDataTable && oColumn && elTh && elTarget) {
  1233. this.datatable = oDataTable;
  1234. this.table = oDataTable.getTableEl();
  1235. this.column = oColumn;
  1236. this.headCell = elTh;
  1237. this.pointer = elTarget;
  1238. this.newIndex = null;
  1239. this.init(elTh);
  1240. this.initFrame(); // Needed for DDProxy
  1241. this.invalidHandleTypes = {};
  1242. // Set top/bottom padding to account for children of nested columns
  1243. this.setPadding(10, 0, (this.datatable.getTheadEl().offsetHeight + 10) , 0);
  1244. YAHOO.util.Event.on(window, 'resize', function() {
  1245. this.initConstraints();
  1246. }, this, true);
  1247. }
  1248. else {
  1249. YAHOO.log("Column dragdrop could not be created","warn",oDataTable.toString());
  1250. }
  1251. };
  1252. if(YAHOO.util.DDProxy) {
  1253. YAHOO.extend(YAHOO.widget.ColumnDD, YAHOO.util.DDProxy, {
  1254. initConstraints: function() {
  1255. //Get the top, right, bottom and left positions
  1256. var region = YAHOO.util.Dom.getRegion(this.table),
  1257. //Get the element we are working on
  1258. el = this.getEl(),
  1259. //Get the xy position of it
  1260. xy = YAHOO.util.Dom.getXY(el),
  1261. //Get the width and height
  1262. width = parseInt(YAHOO.util.Dom.getStyle(el, 'width'), 10),
  1263. height = parseInt(YAHOO.util.Dom.getStyle(el, 'height'), 10),
  1264. //Set left to x minus left
  1265. left = ((xy[0] - region.left) + 15), //Buffer of 15px
  1266. //Set right to right minus x minus width
  1267. right = ((region.right - xy[0] - width) + 15);
  1268. //Set the constraints based on the above calculations
  1269. this.setXConstraint(left, right);
  1270. this.setYConstraint(10, 10);
  1271. },
  1272. _resizeProxy: function() {
  1273. YAHOO.widget.ColumnDD.superclass._resizeProxy.apply(this, arguments);
  1274. var dragEl = this.getDragEl(),
  1275. el = this.getEl();
  1276. YAHOO.util.Dom.setStyle(this.pointer, 'height', (this.table.parentNode.offsetHeight + 10) + 'px');
  1277. YAHOO.util.Dom.setStyle(this.pointer, 'display', 'block');
  1278. var xy = YAHOO.util.Dom.getXY(el);
  1279. YAHOO.util.Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);
  1280. YAHOO.util.Dom.setStyle(dragEl, 'height', this.datatable.getContainerEl().offsetHeight + "px");
  1281. YAHOO.util.Dom.setStyle(dragEl, 'width', (parseInt(YAHOO.util.Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
  1282. YAHOO.util.Dom.setXY(this.dragEl, xy);
  1283. },
  1284. onMouseDown: function() {
  1285. this.initConstraints();
  1286. this.resetConstraints();
  1287. },
  1288. clickValidator: function(e) {
  1289. if(!this.column.hidden) {
  1290. var target = YAHOO.util.Event.getTarget(e);
  1291. return ( this.isValidHandleChild(target) &&
  1292. (this.id == this.handleElId ||
  1293. this.DDM.handleWasClicked(target, this.id)) );
  1294. }
  1295. },
  1296. onDragOver: function(ev, id) {
  1297. // Validate target as a Column
  1298. var target = this.datatable.getColumn(id);
  1299. if(target) {
  1300. // Validate target as a top-level parent
  1301. var targetIndex = target.getTreeIndex();
  1302. while((targetIndex === null) && target.getParent()) {
  1303. target = target.getParent();
  1304. targetIndex = target.getTreeIndex();
  1305. }
  1306. if(targetIndex !== null) {
  1307. // Are we placing to left or right of target?
  1308. var elTarget = target.getThEl();
  1309. var newIndex = targetIndex;
  1310. var mouseX = YAHOO.util.Event.getPageX(ev),
  1311. targetX = YAHOO.util.Dom.getX(elTarget),
  1312. midX = targetX + ((YAHOO.util.Dom.get(elTarget).offsetWidth)/2),
  1313. currentIndex = this.column.getTreeIndex();
  1314. if (mouseX < midX) {
  1315. YAHOO.util.Dom.setX(this.pointer, targetX);
  1316. } else {
  1317. var targetWidth = parseInt(elTarget.offsetWidth, 10);
  1318. YAHOO.util.Dom.setX(this.pointer, (targetX + targetWidth));
  1319. newIndex++;
  1320. }
  1321. if (targetIndex > currentIndex) {
  1322. newIndex--;
  1323. }
  1324. if(newIndex < 0) {
  1325. newIndex = 0;
  1326. }
  1327. else if(newIndex > this.datatable.getColumnSet().tree[0].length) {
  1328. newIndex = this.datatable.getColumnSet().tree[0].length;
  1329. }
  1330. this.newIndex = newIndex;
  1331. }
  1332. }
  1333. },
  1334. onDragDrop: function() {
  1335. this.datatable.reorderColumn(this.column, this.newIndex);
  1336. },
  1337. endDrag: function() {
  1338. this.newIndex = null;
  1339. YAHOO.util.Dom.setStyle(this.pointer, 'display', 'none');
  1340. }
  1341. });
  1342. }
  1343. /****************************************************************************/
  1344. /****************************************************************************/
  1345. /****************************************************************************/
  1346. /**
  1347. * ColumnResizer subclasses DragDrop to support resizeable Columns.
  1348. *
  1349. * @namespace YAHOO.util
  1350. * @class ColumnResizer
  1351. * @extends YAHOO.util.DDProxy
  1352. * @constructor
  1353. * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
  1354. * @param oColumn {YAHOO.widget.Column} Column instance.
  1355. * @param elTh {HTMLElement} TH element reference.
  1356. * @param sHandleElId {String} DOM ID of the handle element that causes the resize.
  1357. * @param elProxy {HTMLElement} Resizer proxy element.
  1358. */
  1359. YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elTh, sHandleId, elProxy) {
  1360. if(oDataTable && oColumn && elTh && sHandleId) {
  1361. this.datatable = oDataTable;
  1362. this.column = oColumn;
  1363. this.headCell = elTh;
  1364. this.headCellLiner = oColumn.getThLinerEl();
  1365. this.resizerLiner = elTh.firstChild;
  1366. this.init(sHandleId, sHandleId, {dragOnly:true, dragElId: elProxy.id});
  1367. this.initFrame(); // Needed for proxy
  1368. this.resetResizerEl(); // Needed when rowspan > 0
  1369. // Set right padding for bug 1858462
  1370. this.setPadding(0, 1, 0, 0);
  1371. }
  1372. else {
  1373. YAHOO.log("Column resizer could not be created","warn",oDataTable.toString());
  1374. }
  1375. };
  1376. if(YAHOO.util.DD) {
  1377. YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DDProxy, {
  1378. /////////////////////////////////////////////////////////////////////////////
  1379. //
  1380. // Public methods
  1381. //
  1382. /////////////////////////////////////////////////////////////////////////////
  1383. /**
  1384. * Resets resizer element.
  1385. *
  1386. * @method resetResizerEl
  1387. */
  1388. resetResizerEl : function() {
  1389. var resizerStyle = YAHOO.util.Dom.get(this.handleElId).style;
  1390. resizerStyle.left = "auto";
  1391. resizerStyle.right = 0;
  1392. resizerStyle.top = "auto";
  1393. resizerStyle.bottom = 0;
  1394. resizerStyle.height = this.headCell.offsetHeight+"px";
  1395. },
  1396. /////////////////////////////////////////////////////////////////////////////
  1397. //
  1398. // Public DOM event handlers
  1399. //
  1400. /////////////////////////////////////////////////////////////////////////////
  1401. /**
  1402. * Handles mouseup events on the Column resizer.
  1403. *
  1404. * @method onMouseUp
  1405. * @param e {string} The mouseup event
  1406. */
  1407. onMouseUp : function(e) {
  1408. // Reset height of all resizer els in case TH's have changed height
  1409. var allKeys = this.datatable.getColumnSet().keys,
  1410. col;
  1411. for(var i=0, len=allKeys.length; i<len; i++) {
  1412. col = allKeys[i];
  1413. if(col._ddResizer) {
  1414. col._ddResizer.resetResizerEl();
  1415. }
  1416. }
  1417. this.resetResizerEl();
  1418. var el = this.headCellLiner;
  1419. var newWidth = el.offsetWidth -
  1420. (parseInt(YAHOO.util.Dom.getStyle(el,"paddingLeft"),10)|0) -
  1421. (parseInt(YAHOO.util.Dom.getStyle(el,"paddingRight"),10)|0);
  1422. this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.headCell,width:newWidth});
  1423. },
  1424. /**
  1425. * Handles mousedown events on the Column resizer.
  1426. *
  1427. * @method onMouseDown
  1428. * @param e {string} The mousedown event
  1429. */
  1430. onMouseDown : function(e) {
  1431. this.startWidth = this.headCellLiner.offsetWidth;
  1432. this.startX = YAHOO.util.Event.getXY(e)[0];
  1433. this.nLinerPadding = (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0) +
  1434. (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0);
  1435. },
  1436. /**
  1437. * Custom clickValidator to ensure Column is not in hidden state.
  1438. *
  1439. * @method clickValidator
  1440. * @param {Event} e
  1441. * @private
  1442. */
  1443. clickValidator : function(e) {
  1444. if(!this.column.hidden) {
  1445. var target = YAHOO.util.Event.getTarget(e);
  1446. return ( this.isValidHandleChild(target) &&
  1447. (this.id == this.handleElId ||
  1448. this.DDM.handleWasClicked(target, this.id)) );
  1449. }
  1450. },
  1451. /**
  1452. * Handles start drag on the Column resizer.
  1453. *
  1454. * @method startDrag
  1455. * @param e {string} The drag event
  1456. */
  1457. startDrag : function() {
  1458. // Shrinks height of all resizer els to not hold open TH els
  1459. var allKeys = this.datatable.getColumnSet().keys,
  1460. thisKey = this.column.getKeyIndex(),
  1461. col;
  1462. for(var i=0, len=allKeys.length; i<len; i++) {
  1463. col = allKeys[i];
  1464. if(col._ddResizer) {
  1465. YAHOO.util.Dom.get(col._ddResizer.handleElId).style.height = "1em";
  1466. }
  1467. }
  1468. },
  1469. /**
  1470. * Handles drag events on the Column resizer.
  1471. *
  1472. * @method onDrag
  1473. * @param e {string} The drag event
  1474. */
  1475. onDrag : function(e) {
  1476. var newX = YAHOO.util.Event.getXY(e)[0];
  1477. if(newX > YAHOO.util.Dom.getX(this.headCellLiner)) {
  1478. var offsetX = newX - this.startX;
  1479. var newWidth = this.startWidth + offsetX - this.nLinerPadding;
  1480. if(newWidth > 0) {
  1481. this.datatable.setColumnWidth(this.column, newWidth);
  1482. }
  1483. }
  1484. }
  1485. });
  1486. }
  1487. /////////////////////////////////////////////////////////////////////////////
  1488. //
  1489. // Deprecated
  1490. //
  1491. /////////////////////////////////////////////////////////////////////////////
  1492. /**
  1493. * @property editorOptions
  1494. * @deprecated Pass configs directly to CellEditor constructor.
  1495. */
  1496. (function () {
  1497. var lang = YAHOO.lang,
  1498. util = YAHOO.util,
  1499. widget = YAHOO.widget,
  1500. Dom = util.Dom,
  1501. Ev = util.Event,
  1502. DT = widget.DataTable;
  1503. /****************************************************************************/
  1504. /****************************************************************************/
  1505. /****************************************************************************/
  1506. /**
  1507. * A RecordSet defines and manages a set of Records.
  1508. *
  1509. * @namespace YAHOO.widget
  1510. * @class RecordSet
  1511. * @param data {Object || Object[]} An object literal or an array of data.
  1512. * @constructor
  1513. */
  1514. YAHOO.widget.RecordSet = function(data) {
  1515. // Internal variables
  1516. this._sId = "yui-rs" + widget.RecordSet._nCount;
  1517. widget.RecordSet._nCount++;
  1518. this._records = [];
  1519. //this._length = 0;
  1520. if(data) {
  1521. if(lang.isArray(data)) {
  1522. this.addRecords(data);
  1523. }
  1524. else if(lang.isObject(data)) {
  1525. this.addRecord(data);
  1526. }
  1527. }
  1528. YAHOO.log("RecordSet initialized", "info", this.toString());
  1529. };
  1530. var RS = widget.RecordSet;
  1531. /**
  1532. * Internal class variable to name multiple Recordset instances.
  1533. *
  1534. * @property RecordSet._nCount
  1535. * @type Number
  1536. * @private
  1537. * @static
  1538. */
  1539. RS._nCount = 0;
  1540. RS.prototype = {
  1541. /////////////////////////////////////////////////////////////////////////////
  1542. //
  1543. // Private member variables
  1544. //
  1545. /////////////////////////////////////////////////////////////////////////////
  1546. /**
  1547. * Unique String identifier assigned at instantiation.
  1548. *
  1549. * @property _sId
  1550. * @type String
  1551. * @private
  1552. */
  1553. _sId : null,
  1554. /**
  1555. * Internal counter of how many Records are in the RecordSet.
  1556. *
  1557. * @property _length
  1558. * @type Number
  1559. * @private
  1560. * @deprecated No longer used
  1561. */
  1562. //_length : null,
  1563. /////////////////////////////////////////////////////////////////////////////
  1564. //
  1565. // Private methods
  1566. //
  1567. /////////////////////////////////////////////////////////////////////////////
  1568. /**
  1569. * Adds one Record to the RecordSet at the given index. If index is null,
  1570. * then adds the Record to the end of the RecordSet.
  1571. *
  1572. * @method _addRecord
  1573. * @param oData {Object} An object literal of data.
  1574. * @param index {Number} (optional) Position index.
  1575. * @return {YAHOO.widget.Record} A Record instance.
  1576. * @private
  1577. */
  1578. _addRecord : function(oData, index) {
  1579. var oRecord = new YAHOO.widget.Record(oData);
  1580. if(YAHOO.lang.isNumber(index) && (index > -1)) {
  1581. this._records.splice(index,0,oRecord);
  1582. }
  1583. else {
  1584. //index = this.getLength();
  1585. //this._records[index] = oRecord;
  1586. this._records[this._records.length] = oRecord;
  1587. }
  1588. //this._length++;
  1589. return oRecord;
  1590. },
  1591. /**
  1592. * Sets/replaces one Record to the RecordSet at the given index. Existing
  1593. * Records with higher indexes are not shifted. If no index specified, the
  1594. * Record is added to the end of the RecordSet.
  1595. *
  1596. * @method _setRecord
  1597. * @param oData {Object} An object literal of data.
  1598. * @param index {Number} (optional) Position index.
  1599. * @return {YAHOO.widget.Record} A Record instance.
  1600. * @private
  1601. */
  1602. _setRecord : function(oData, index) {
  1603. if (!lang.isNumber(index) || index < 0) {
  1604. index = this._records.length;
  1605. }
  1606. return (this._records[index] = new widget.Record(oData));
  1607. /*
  1608. if(lang.isNumber(index) && (index > -1)) {
  1609. this._records[index] = oRecord;
  1610. if((index+1) > this.getLength()) {
  1611. this._length = index+1;
  1612. }
  1613. }
  1614. else {
  1615. this._records[this.getLength()] = oRecord;
  1616. this._length++;
  1617. }
  1618. return oRecord;
  1619. */
  1620. },
  1621. /**
  1622. * Deletes Records from the RecordSet at the given index. If range is null,
  1623. * then only one Record is deleted.
  1624. *
  1625. * @method _deleteRecord
  1626. * @param index {Number} Position index.
  1627. * @param range {Number} (optional) How many Records to delete
  1628. * @private
  1629. */
  1630. _deleteRecord : function(index, range) {
  1631. if(!lang.isNumber(range) || (range < 0)) {
  1632. range = 1;
  1633. }
  1634. this._records.splice(index, range);
  1635. //this._length = this._length - range;
  1636. },
  1637. /////////////////////////////////////////////////////////////////////////////
  1638. //
  1639. // Public methods
  1640. //
  1641. /////////////////////////////////////////////////////////////////////////////
  1642. /**
  1643. * Returns unique name of the RecordSet instance.
  1644. *
  1645. * @method getId
  1646. * @return {String} Unique name of the RecordSet instance.
  1647. */
  1648. getId : function() {
  1649. return this._sId;
  1650. },
  1651. /**
  1652. * Public accessor to the unique name of the RecordSet instance.
  1653. *
  1654. * @method toString
  1655. * @return {String} Unique name of the RecordSet instance.
  1656. */
  1657. toString : function() {
  1658. return "RecordSet instance " + this._sId;
  1659. },
  1660. /**
  1661. * Returns the number of Records held in the RecordSet.
  1662. *
  1663. * @method getLength
  1664. * @return {Number} Number of records in the RecordSet.
  1665. */
  1666. getLength : function() {
  1667. //return this._length;
  1668. return this._records.length;
  1669. },
  1670. /**
  1671. * Returns Record by ID or RecordSet position index.
  1672. *
  1673. * @method getRecord
  1674. * @param record {YAHOO.widget.Record | Number | String} Record instance,
  1675. * RecordSet position index, or Record ID.
  1676. * @return {YAHOO.widget.Record} Record object.
  1677. */
  1678. getRecord : function(record) {
  1679. var i;
  1680. if(record instanceof widget.Record) {
  1681. for(i=0; i<this._records.length; i++) {
  1682. if(this._records[i] && (this._records[i]._sId === record._sId)) {
  1683. return record;
  1684. }
  1685. }
  1686. }
  1687. else if(lang.isNumber(record)) {
  1688. if((record > -1) && (record < this.getLength())) {
  1689. return this._records[record];
  1690. }
  1691. }
  1692. else if(lang.isString(record)) {
  1693. for(i=0; i<this._records.length; i++) {
  1694. if(this._records[i] && (this._records[i]._sId === record)) {
  1695. return this._records[i];
  1696. }
  1697. }
  1698. }
  1699. // Not a valid Record for this RecordSet
  1700. return null;
  1701. },
  1702. /**
  1703. * Returns an array of Records from the RecordSet.
  1704. *
  1705. * @method getRecords
  1706. * @param index {Number} (optional) Recordset position index of which Record to
  1707. * start at.
  1708. * @param range {Number} (optional) Number of Records to get.
  1709. * @return {YAHOO.widget.Record[]} Array of Records starting at given index and
  1710. * length equal to given range. If index is not given, all Records are returned.
  1711. */
  1712. getRecords : function(index, range) {
  1713. if(!lang.isNumber(index)) {
  1714. return this._records;
  1715. }
  1716. if(!lang.isNumber(range)) {
  1717. return this._records.slice(index);
  1718. }
  1719. return this._records.slice(index, index+range);
  1720. },
  1721. /**
  1722. * Returns a boolean indicating whether Records exist in the RecordSet at the
  1723. * specified index range. Returns true if and only if a Record exists at each
  1724. * index in the range.
  1725. * @method hasRecords
  1726. * @param index
  1727. * @param range
  1728. * @return {Boolean} true if all indices are populated in the RecordSet
  1729. */
  1730. hasRecords : function (index, range) {
  1731. var recs = this.getRecords(index,range);
  1732. for (var i = 0; i < range; ++i) {
  1733. if (typeof recs[i] === 'undefined') {
  1734. return false;
  1735. }
  1736. }
  1737. return true;
  1738. },
  1739. /**
  1740. * Returns current position index for the given Record.
  1741. *
  1742. * @method getRecordIndex
  1743. * @param oRecord {YAHOO.widget.Record} Record instance.
  1744. * @return {Number} Record's RecordSet position index.
  1745. */
  1746. getRecordIndex : function(oRecord) {
  1747. if(oRecord) {
  1748. for(var i=this._records.length-1; i>-1; i--) {
  1749. if(this._records[i] && oRecord.getId() === this._records[i].getId()) {
  1750. return i;
  1751. }
  1752. }
  1753. }
  1754. return null;
  1755. },
  1756. /**
  1757. * Adds one Record to the RecordSet at the given index. If index is null,
  1758. * then adds the Record to the end of the RecordSet.
  1759. *
  1760. * @method addRecord
  1761. * @param oData {Object} An object literal of data.
  1762. * @param index {Number} (optional) Position index.
  1763. * @return {YAHOO.widget.Record} A Record instance.
  1764. */
  1765. addRecord : function(oData, index) {
  1766. if(lang.isObject(oData)) {
  1767. var oRecord = this._addRecord(oData, index);
  1768. this.fireEvent("recordAddEvent",{record:oRecord,data:oData});
  1769. YAHOO.log("Added Record at index " + index +
  1770. " with data " + lang.dump(oData), "info", this.toString());
  1771. return oRecord;
  1772. }
  1773. else {
  1774. YAHOO.log("Could not add Record with data" +
  1775. lang.dump(oData), "info", this.toString());
  1776. return null;
  1777. }
  1778. },
  1779. /**
  1780. * Adds multiple Records at once to the RecordSet at the given index with the
  1781. * given object literal data. If index is null, then the new Records are
  1782. * added to the end of the RecordSet.
  1783. *
  1784. * @method addRecords
  1785. * @param aData {Object[]} An object literal data or an array of data object literals.
  1786. * @param index {Number} (optional) Position index.
  1787. * @return {YAHOO.widget.Record[]} An array of Record instances.
  1788. */
  1789. addRecords : function(aData, index) {
  1790. if(lang.isArray(aData)) {
  1791. var newRecords = [],
  1792. idx,i,len;
  1793. index = lang.isNumber(index) ? index : this._records.length;
  1794. idx = index;
  1795. // Can't go backwards bc we need to preserve order
  1796. for(i=0,len=aData.length; i<len; ++i) {
  1797. if(lang.isObject(aData[i])) {
  1798. var record = this._addRecord(aData[i], idx++);
  1799. newRecords.push(record);
  1800. }
  1801. }
  1802. this.fireEvent("recordsAddEvent",{records:newRecords,data:aData});
  1803. YAHOO.log("Added " + newRecords.length + " Record(s) at index " + index +
  1804. " with data " + lang.dump(aData), "info", this.toString());
  1805. return newRecords;
  1806. }
  1807. else if(lang.isObject(aData)) {
  1808. var oRecord = this._addRecord(aData);
  1809. this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData});
  1810. YAHOO.log("Added 1 Record at index " + index +
  1811. " with data " + lang.dump(aData), "info", this.toString());
  1812. return oRecord;
  1813. }
  1814. else {
  1815. YAHOO.log("Could not add Records with data " +
  1816. lang.dump(aData), "info", this.toString());
  1817. return null;
  1818. }
  1819. },
  1820. /**
  1821. * Sets or replaces one Record to the RecordSet at the given index. Unlike
  1822. * addRecord, an existing Record at that index is not shifted to preserve it.
  1823. * If no index is specified, it adds the Record to the end of the RecordSet.
  1824. *
  1825. * @method setRecord
  1826. * @param oData {Object} An object literal of data.
  1827. * @param index {Number} (optional) Position index.
  1828. * @return {YAHOO.widget.Record} A Record instance.
  1829. */
  1830. setRecord : function(oData, index) {
  1831. if(lang.isObject(oData)) {
  1832. var oRecord = this._setRecord(oData, index);
  1833. this.fireEvent("recordSetEvent",{record:oRecord,data:oData});
  1834. YAHOO.log("Set Record at index " + index +
  1835. " with data " + lang.dump(oData), "info", this.toString());
  1836. return oRecord;
  1837. }
  1838. else {
  1839. YAHOO.log("Could not set Record with data" +
  1840. lang.dump(oData), "info", this.toString());
  1841. return null;
  1842. }
  1843. },
  1844. /**
  1845. * Sets or replaces multiple Records at once to the RecordSet with the given
  1846. * data, starting at the given index. If index is not specified, then the new
  1847. * Records are added to the end of the RecordSet.
  1848. *
  1849. * @method setRecords
  1850. * @param aData {Object[]} An array of object literal data.
  1851. * @param index {Number} (optional) Position index.
  1852. * @return {YAHOO.widget.Record[]} An array of Record instances.
  1853. */
  1854. setRecords : function(aData, index) {
  1855. var Rec = widget.Record,
  1856. a = lang.isArray(aData) ? aData : [aData],
  1857. added = [],
  1858. i = 0, l = a.length, j = 0;
  1859. index = parseInt(index,10)|0;
  1860. for(; i < l; ++i) {
  1861. if (typeof a[i] === 'object' && a[i]) {
  1862. added[j++] = this._records[index + i] = new Rec(a[i]);
  1863. }
  1864. }
  1865. this.fireEvent("recordsSetEvent",{records:added,data:aData});
  1866. // Backward compatibility for bug 1918245
  1867. this.fireEvent("recordsSet",{records:added,data:aData});
  1868. YAHOO.log("Set "+j+" Record(s) at index "+index, "info",
  1869. this.toString());
  1870. if (a.length && !added.length) {
  1871. YAHOO.log("Could not set Records with data " +
  1872. lang.dump(aData), "info", this.toString());
  1873. }
  1874. return added.length > 1 ? added : added[0];
  1875. },
  1876. /**
  1877. * Updates given Record with given data.
  1878. *
  1879. * @method updateRecord
  1880. * @param record {YAHOO.widget.Record | Number | String} A Record instance,
  1881. * a RecordSet position index, or a Record ID.
  1882. * @param oData {Object} Object literal of new data.
  1883. * @return {YAHOO.widget.Record} Updated Record, or null.
  1884. */
  1885. updateRecord : function(record, oData) {
  1886. var oRecord = this.getRecord(record);
  1887. if(oRecord && lang.isObject(oData)) {
  1888. // Copy data from the Record for the event that gets fired later
  1889. var oldData = {};
  1890. for(var key in oRecord._oData) {
  1891. if(lang.hasOwnProperty(oRecord._oData, key)) {
  1892. oldData[key] = oRecord._oData[key];
  1893. }
  1894. }
  1895. oRecord._oData = oData;
  1896. this.fireEvent("recordUpdateEvent",{record:oRecord,newData:oData,oldData:oldData});
  1897. YAHOO.log("Record at index " + this.getRecordIndex(oRecord) +
  1898. " updated with data " + lang.dump(oData), "info", this.toString());
  1899. return oRecord;
  1900. }
  1901. else {
  1902. YAHOO.log("Could not update Record " + record, "error", this.toString());
  1903. return null;
  1904. }
  1905. },
  1906. /**
  1907. * @method updateKey
  1908. * @deprecated Use updateRecordValue
  1909. */
  1910. updateKey : function(record, sKey, oData) {
  1911. this.updateRecordValue(record, sKey, oData);
  1912. },
  1913. /**
  1914. * Sets given Record at given key to given data.
  1915. *
  1916. * @method updateRecordValue
  1917. * @param record {YAHOO.widget.Record | Number | String} A Record instance,
  1918. * a RecordSet position index, or a Record ID.
  1919. * @param sKey {String} Key name.
  1920. * @param oData {Object} New data.
  1921. */
  1922. updateRecordValue : function(record, sKey, oData) {
  1923. var oRecord = this.getRecord(record);
  1924. if(oRecord) {
  1925. var oldData = null;
  1926. var keyValue = oRecord._oData[sKey];
  1927. // Copy data from the Record for the event that gets fired later
  1928. if(keyValue && lang.isObject(keyValue)) {
  1929. oldData = {};
  1930. for(var key in keyValue) {
  1931. if(lang.hasOwnProperty(keyValue, key)) {
  1932. oldData[key] = keyValue[key];
  1933. }
  1934. }
  1935. }
  1936. // Copy by value
  1937. else {
  1938. oldData = keyValue;
  1939. }
  1940. oRecord._oData[sKey] = oData;
  1941. this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
  1942. this.fireEvent("recordValueUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
  1943. YAHOO.log("Key \"" + sKey +
  1944. "\" for Record at index " + this.getRecordIndex(oRecord) +
  1945. " updated to \"" + lang.dump(oData) + "\"", "info", this.toString());
  1946. }
  1947. else {
  1948. YAHOO.log("Could not update key " + sKey + " for Record " + record, "error", this.toString());
  1949. }
  1950. },
  1951. /**
  1952. * Replaces all Records in RecordSet with new object literal data.
  1953. *
  1954. * @method replaceRecords
  1955. * @param data {Object || Object[]} An object literal of data or an array of
  1956. * data object literals.
  1957. * @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record instance or
  1958. * an array of Records.
  1959. */
  1960. replaceRecords : function(data) {
  1961. this.reset();
  1962. return this.addRecords(data);
  1963. },
  1964. /**
  1965. * Sorts all Records by given function. Records keep their unique IDs but will
  1966. * have new RecordSet position indexes.
  1967. *
  1968. * @method sortRecords
  1969. * @param fnSort {Function} Reference to a sort function.
  1970. * @param desc {Boolean} True if sort direction is descending, false if sort
  1971. * direction is ascending.
  1972. * @param field {String} The field to sort by, from sortOptions.field
  1973. * @return {YAHOO.widget.Record[]} Sorted array of Records.
  1974. */
  1975. sortRecords : function(fnSort, desc, field) {
  1976. return this._records.sort(function(a, b) {return fnSort(a, b, desc, field);});
  1977. },
  1978. /**
  1979. * Reverses all Records, so ["one", "two", "three"] becomes ["three", "two", "one"].
  1980. *
  1981. * @method reverseRecords
  1982. * @return {YAHOO.widget.Record[]} Reverse-sorted array of Records.
  1983. */
  1984. reverseRecords : function() {
  1985. return this._records.reverse();
  1986. },
  1987. /**
  1988. * Removes the Record at the given position index from the RecordSet. If a range
  1989. * is also provided, removes that many Records, starting from the index. Length
  1990. * of RecordSet is correspondingly shortened.
  1991. *
  1992. * @method deleteRecord
  1993. * @param index {Number} Record's RecordSet position index.
  1994. * @param range {Number} (optional) How many Records to delete.
  1995. * @return {Object} A copy of the data held by the deleted Record.
  1996. */
  1997. deleteRecord : function(index) {
  1998. if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
  1999. // Copy data from the Record for the event that gets fired later
  2000. var oData = widget.DataTable._cloneObject(this.getRecord(index).getData());
  2001. this._deleteRecord(index);
  2002. this.fireEvent("recordDeleteEvent",{data:oData,index:index});
  2003. YAHOO.log("Record deleted at index " + index +
  2004. " and containing data " + lang.dump(oData), "info", this.toString());
  2005. return oData;
  2006. }
  2007. else {
  2008. YAHOO.log("Could not delete Record at index " + index, "error", this.toString());
  2009. return null;
  2010. }
  2011. },
  2012. /**
  2013. * Removes the Record at the given position index from the RecordSet. If a range
  2014. * is also provided, removes that many Records, starting from the index. Length
  2015. * of RecordSet is correspondingly shortened.
  2016. *
  2017. * @method deleteRecords
  2018. * @param index {Number} Record's RecordSet position index.
  2019. * @param range {Number} (optional) How many Records to delete.
  2020. * @return {Object[]} An array of copies of the data held by the deleted Records.
  2021. */
  2022. deleteRecords : function(index, range) {
  2023. if(!lang.isNumber(range)) {
  2024. range = 1;
  2025. }
  2026. if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
  2027. var recordsToDelete = this.getRecords(index, range);
  2028. // Copy data from each Record for the event that gets fired later
  2029. var deletedData = [];
  2030. for(var i=0; i<recordsToDelete.length; i++) {
  2031. deletedData[deletedData.length] = widget.DataTable._cloneObject(recordsToDelete[i]);
  2032. }
  2033. this._deleteRecord(index, range);
  2034. this.fireEvent("recordsDeleteEvent",{data:deletedData,index:index});
  2035. YAHOO.log(range + "Record(s) deleted at index " + index +
  2036. " and containing data " + lang.dump(deletedData), "info", this.toString());
  2037. return deletedData;
  2038. }
  2039. else {
  2040. YAHOO.log("Could not delete Records at index " + index, "error", this.toString());
  2041. return null;
  2042. }
  2043. },
  2044. /**
  2045. * Deletes all Records from the RecordSet.
  2046. *
  2047. * @method reset
  2048. */
  2049. reset : function() {
  2050. this._records = [];
  2051. //this._length = 0;
  2052. this.fireEvent("resetEvent");
  2053. YAHOO.log("All Records deleted from RecordSet", "info", this.toString());
  2054. }
  2055. };
  2056. /////////////////////////////////////////////////////////////////////////////
  2057. //
  2058. // Custom Events
  2059. //
  2060. /////////////////////////////////////////////////////////////////////////////
  2061. // RecordSet uses EventProvider
  2062. lang.augmentProto(RS, util.EventProvider);
  2063. /**
  2064. * Fired when a new Record is added to the RecordSet.
  2065. *
  2066. * @event recordAddEvent
  2067. * @param oArgs.record {YAHOO.widget.Record} The Record instance.
  2068. * @param oArgs.data {Object} Data added.
  2069. */
  2070. /**
  2071. * Fired when multiple Records are added to the RecordSet at once.
  2072. *
  2073. * @event recordsAddEvent
  2074. * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
  2075. * @param oArgs.data {Object[]} Data added.
  2076. */
  2077. /**
  2078. * Fired when a Record is set in the RecordSet.
  2079. *
  2080. * @event recordSetEvent
  2081. * @param oArgs.record {YAHOO.widget.Record} The Record instance.
  2082. * @param oArgs.data {Object} Data added.
  2083. */
  2084. /**
  2085. * Fired when multiple Records are set in the RecordSet at once.
  2086. *
  2087. * @event recordsSetEvent
  2088. * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
  2089. * @param oArgs.data {Object[]} Data added.
  2090. */
  2091. /**
  2092. * Fired when a Record is updated with new data.
  2093. *
  2094. * @event recordUpdateEvent
  2095. * @param oArgs.record {YAHOO.widget.Record} The Record instance.
  2096. * @param oArgs.newData {Object} New data.
  2097. * @param oArgs.oldData {Object} Old data.
  2098. */
  2099. /**
  2100. * Fired when a Record is deleted from the RecordSet.
  2101. *
  2102. * @event recordDeleteEvent
  2103. * @param oArgs.data {Object} A copy of the data held by the Record,
  2104. * or an array of data object literals if multiple Records were deleted at once.
  2105. * @param oArgs.index {Object} Index of the deleted Record.
  2106. */
  2107. /**
  2108. * Fired when multiple Records are deleted from the RecordSet at once.
  2109. *
  2110. * @event recordsDeleteEvent
  2111. * @param oArgs.data {Object[]} An array of data object literals copied
  2112. * from the Records.
  2113. * @param oArgs.index {Object} Index of the first deleted Record.
  2114. */
  2115. /**
  2116. * Fired when all Records are deleted from the RecordSet at once.
  2117. *
  2118. * @event resetEvent
  2119. */
  2120. /**
  2121. * @event keyUpdateEvent
  2122. * @deprecated Use recordValueUpdateEvent
  2123. */
  2124. /**
  2125. * Fired when a Record value is updated with new data.
  2126. *
  2127. * @event recordValueUpdateEvent
  2128. * @param oArgs.record {YAHOO.widget.Record} The Record instance.
  2129. * @param oArgs.key {String} The updated key.
  2130. * @param oArgs.newData {Object} New data.
  2131. * @param oArgs.oldData {Object} Old data.
  2132. *
  2133. */
  2134. /****************************************************************************/
  2135. /****************************************************************************/
  2136. /****************************************************************************/
  2137. /**
  2138. * The Record class defines a DataTable record.
  2139. *
  2140. * @namespace YAHOO.widget
  2141. * @class Record
  2142. * @constructor
  2143. * @param oConfigs {Object} (optional) Object literal of key/value pairs.
  2144. */
  2145. YAHOO.widget.Record = function(oLiteral) {
  2146. this._nCount = widget.Record._nCount;
  2147. this._sId = "yui-rec" + this._nCount;
  2148. widget.Record._nCount++;
  2149. this._oData = {};
  2150. if(lang.isObject(oLiteral)) {
  2151. for(var sKey in oLiteral) {
  2152. if(lang.hasOwnProperty(oLiteral, sKey)) {
  2153. this._oData[sKey] = oLiteral[sKey];
  2154. }
  2155. }
  2156. }
  2157. };
  2158. /////////////////////////////////////////////////////////////////////////////
  2159. //
  2160. // Private member variables
  2161. //
  2162. /////////////////////////////////////////////////////////////////////////////
  2163. /**
  2164. * Internal class variable to give unique IDs to Record instances.
  2165. *
  2166. * @property Record._nCount
  2167. * @type Number
  2168. * @private
  2169. */
  2170. YAHOO.widget.Record._nCount = 0;
  2171. YAHOO.widget.Record.prototype = {
  2172. /**
  2173. * Immutable unique count assigned at instantiation. Remains constant while a
  2174. * Record's position index can change from sorting.
  2175. *
  2176. * @property _nCount
  2177. * @type Number
  2178. * @private
  2179. */
  2180. _nCount : null,
  2181. /**
  2182. * Immutable unique ID assigned at instantiation. Remains constant while a
  2183. * Record's position index can change from sorting.
  2184. *
  2185. * @property _sId
  2186. * @type String
  2187. * @private
  2188. */
  2189. _sId : null,
  2190. /**
  2191. * Holds data for the Record in an object literal.
  2192. *
  2193. * @property _oData
  2194. * @type Object
  2195. * @private
  2196. */
  2197. _oData : null,
  2198. /////////////////////////////////////////////////////////////////////////////
  2199. //
  2200. // Public member variables
  2201. //
  2202. /////////////////////////////////////////////////////////////////////////////
  2203. /////////////////////////////////////////////////////////////////////////////
  2204. //
  2205. // Public methods
  2206. //
  2207. /////////////////////////////////////////////////////////////////////////////
  2208. /**
  2209. * Returns unique count assigned at instantiation.
  2210. *
  2211. * @method getCount
  2212. * @return Number
  2213. */
  2214. getCount : function() {
  2215. return this._nCount;
  2216. },
  2217. /**
  2218. * Returns unique ID assigned at instantiation.
  2219. *
  2220. * @method getId
  2221. * @return String
  2222. */
  2223. getId : function() {
  2224. return this._sId;
  2225. },
  2226. /**
  2227. * Returns data for the Record for a field if given, or the entire object
  2228. * literal otherwise.
  2229. *
  2230. * @method getData
  2231. * @param sField {String} (Optional) The field from which to retrieve data value.
  2232. * @return Object
  2233. */
  2234. getData : function(sField) {
  2235. if(lang.isString(sField)) {
  2236. return this._oData[sField];
  2237. }
  2238. else {
  2239. return this._oData;
  2240. }
  2241. },
  2242. /**
  2243. * Sets given data at the given key. Use the RecordSet method updateRecordValue to trigger
  2244. * events.
  2245. *
  2246. * @method setData
  2247. * @param sKey {String} The key of the new value.
  2248. * @param oData {MIXED} The new value.
  2249. */
  2250. setData : function(sKey, oData) {
  2251. this._oData[sKey] = oData;
  2252. }
  2253. };
  2254. })();
  2255. (function () {
  2256. var lang = YAHOO.lang,
  2257. util = YAHOO.util,
  2258. widget = YAHOO.widget,
  2259. ua = YAHOO.env.ua,
  2260. Dom = util.Dom,
  2261. Ev = util.Event,
  2262. DS = util.DataSourceBase;
  2263. /**
  2264. * The DataTable widget provides a progressively enhanced DHTML control for
  2265. * displaying tabular data across A-grade browsers.
  2266. *
  2267. * @module datatable
  2268. * @requires yahoo, dom, event, element, datasource
  2269. * @optional dragdrop, dragdrop
  2270. * @title DataTable Widget
  2271. */
  2272. /****************************************************************************/
  2273. /****************************************************************************/
  2274. /****************************************************************************/
  2275. /**
  2276. * DataTable class for the YUI DataTable widget.
  2277. *
  2278. * @namespace YAHOO.widget
  2279. * @class DataTable
  2280. * @extends YAHOO.util.Element
  2281. * @constructor
  2282. * @param elContainer {HTMLElement} Container element for the TABLE.
  2283. * @param aColumnDefs {Object[]} Array of object literal Column definitions.
  2284. * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
  2285. * @param oConfigs {object} (optional) Object literal of configuration values.
  2286. */
  2287. YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
  2288. var DT = widget.DataTable;
  2289. ////////////////////////////////////////////////////////////////////////////
  2290. // Backward compatibility for SDT, but prevent infinite loops
  2291. if(oConfigs && oConfigs.scrollable) {
  2292. return new YAHOO.widget.ScrollingDataTable(elContainer,aColumnDefs,oDataSource,oConfigs);
  2293. }
  2294. ////////////////////////////////////////////////////////////////////////////
  2295. // Initialization
  2296. // Internal vars
  2297. this._nIndex = DT._nCount;
  2298. this._sId = "yui-dt"+this._nIndex;
  2299. this._oChainRender = new YAHOO.util.Chain();
  2300. this._oChainRender.subscribe("end",this._onRenderChainEnd, this, true);
  2301. // Initialize configs
  2302. this._initConfigs(oConfigs);
  2303. // Initialize DataSource
  2304. this._initDataSource(oDataSource);
  2305. if(!this._oDataSource) {
  2306. YAHOO.log("Could not instantiate DataTable due to an invalid DataSource", "error", this.toString());
  2307. return;
  2308. }
  2309. // Initialize ColumnSet
  2310. this._initColumnSet(aColumnDefs);
  2311. if(!this._oColumnSet) {
  2312. YAHOO.log("Could not instantiate DataTable due to an invalid ColumnSet", "error", this.toString());
  2313. return;
  2314. }
  2315. // Initialize RecordSet
  2316. this._initRecordSet();
  2317. if(!this._oRecordSet) {
  2318. }
  2319. // Initialize Attributes
  2320. DT.superclass.constructor.call(this, elContainer, this.configs);
  2321. // Initialize DOM elements
  2322. var okDom = this._initDomElements(elContainer);
  2323. if(!okDom) {
  2324. YAHOO.log("Could not instantiate DataTable due to an invalid DOM element", "error", this.toString());
  2325. return;
  2326. }
  2327. // Show message as soon as config is available
  2328. this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
  2329. ////////////////////////////////////////////////////////////////////////////
  2330. // Once per instance
  2331. this._initEvents();
  2332. DT._nCount++;
  2333. DT._nCurrentCount++;
  2334. ////////////////////////////////////////////////////////////////////////////
  2335. // Data integration
  2336. // Send a simple initial request
  2337. var oCallback = {
  2338. success : this.onDataReturnSetRows,
  2339. failure : this.onDataReturnSetRows,
  2340. scope : this,
  2341. argument: this.getState()
  2342. };
  2343. var initialLoad = this.get("initialLoad");
  2344. if(initialLoad === true) {
  2345. this._oDataSource.sendRequest(this.get("initialRequest"), oCallback);
  2346. }
  2347. // Do not send an initial request at all
  2348. else if(initialLoad === false) {
  2349. this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
  2350. }
  2351. // Send an initial request with a custom payload
  2352. else {
  2353. var oCustom = initialLoad || {};
  2354. oCallback.argument = oCustom.argument || {};
  2355. this._oDataSource.sendRequest(oCustom.request, oCallback);
  2356. }
  2357. };
  2358. var DT = widget.DataTable;
  2359. /////////////////////////////////////////////////////////////////////////////
  2360. //
  2361. // Public constants
  2362. //
  2363. /////////////////////////////////////////////////////////////////////////////
  2364. lang.augmentObject(DT, {
  2365. /**
  2366. * Class name assigned to outer DataTable container.
  2367. *
  2368. * @property DataTable.CLASS_DATATABLE
  2369. * @type String
  2370. * @static
  2371. * @final
  2372. * @default "yui-dt"
  2373. */
  2374. CLASS_DATATABLE : "yui-dt",
  2375. /**
  2376. * Class name assigned to liner DIV elements.
  2377. *
  2378. * @property DataTable.CLASS_LINER
  2379. * @type String
  2380. * @static
  2381. * @final
  2382. * @default "yui-dt-liner"
  2383. */
  2384. CLASS_LINER : "yui-dt-liner",
  2385. /**
  2386. * Class name assigned to display label elements.
  2387. *
  2388. * @property DataTable.CLASS_LABEL
  2389. * @type String
  2390. * @static
  2391. * @final
  2392. * @default "yui-dt-label"
  2393. */
  2394. CLASS_LABEL : "yui-dt-label",
  2395. /**
  2396. * Class name assigned to messaging elements.
  2397. *
  2398. * @property DataTable.CLASS_MESSAGE
  2399. * @type String
  2400. * @static
  2401. * @final
  2402. * @default "yui-dt-message"
  2403. */
  2404. CLASS_MESSAGE : "yui-dt-message",
  2405. /**
  2406. * Class name assigned to mask element when DataTable is disabled.
  2407. *
  2408. * @property DataTable.CLASS_MASK
  2409. * @type String
  2410. * @static
  2411. * @final
  2412. * @default "yui-dt-mask"
  2413. */
  2414. CLASS_MASK : "yui-dt-mask",
  2415. /**
  2416. * Class name assigned to data elements.
  2417. *
  2418. * @property DataTable.CLASS_DATA
  2419. * @type String
  2420. * @static
  2421. * @final
  2422. * @default "yui-dt-data"
  2423. */
  2424. CLASS_DATA : "yui-dt-data",
  2425. /**
  2426. * Class name assigned to Column drag target.
  2427. *
  2428. * @property DataTable.CLASS_COLTARGET
  2429. * @type String
  2430. * @static
  2431. * @final
  2432. * @default "yui-dt-coltarget"
  2433. */
  2434. CLASS_COLTARGET : "yui-dt-coltarget",
  2435. /**
  2436. * Class name assigned to resizer handle elements.
  2437. *
  2438. * @property DataTable.CLASS_RESIZER
  2439. * @type String
  2440. * @static
  2441. * @final
  2442. * @default "yui-dt-resizer"
  2443. */
  2444. CLASS_RESIZER : "yui-dt-resizer",
  2445. /**
  2446. * Class name assigned to resizer liner elements.
  2447. *
  2448. * @property DataTable.CLASS_RESIZERLINER
  2449. * @type String
  2450. * @static
  2451. * @final
  2452. * @default "yui-dt-resizerliner"
  2453. */
  2454. CLASS_RESIZERLINER : "yui-dt-resizerliner",
  2455. /**
  2456. * Class name assigned to resizer proxy elements.
  2457. *
  2458. * @property DataTable.CLASS_RESIZERPROXY
  2459. * @type String
  2460. * @static
  2461. * @final
  2462. * @default "yui-dt-resizerproxy"
  2463. */
  2464. CLASS_RESIZERPROXY : "yui-dt-resizerproxy",
  2465. /**
  2466. * Class name assigned to CellEditor container elements.
  2467. *
  2468. * @property DataTable.CLASS_EDITOR
  2469. * @type String
  2470. * @static
  2471. * @final
  2472. * @default "yui-dt-editor"
  2473. */
  2474. CLASS_EDITOR : "yui-dt-editor",
  2475. /**
  2476. * Class name assigned to paginator container elements.
  2477. *
  2478. * @property DataTable.CLASS_PAGINATOR
  2479. * @type String
  2480. * @static
  2481. * @final
  2482. * @default "yui-dt-paginator"
  2483. */
  2484. CLASS_PAGINATOR : "yui-dt-paginator",
  2485. /**
  2486. * Class name assigned to page number indicators.
  2487. *
  2488. * @property DataTable.CLASS_PAGE
  2489. * @type String
  2490. * @static
  2491. * @final
  2492. * @default "yui-dt-page"
  2493. */
  2494. CLASS_PAGE : "yui-dt-page",
  2495. /**
  2496. * Class name assigned to default indicators.
  2497. *
  2498. * @property DataTable.CLASS_DEFAULT
  2499. * @type String
  2500. * @static
  2501. * @final
  2502. * @default "yui-dt-default"
  2503. */
  2504. CLASS_DEFAULT : "yui-dt-default",
  2505. /**
  2506. * Class name assigned to previous indicators.
  2507. *
  2508. * @property DataTable.CLASS_PREVIOUS
  2509. * @type String
  2510. * @static
  2511. * @final
  2512. * @default "yui-dt-previous"
  2513. */
  2514. CLASS_PREVIOUS : "yui-dt-previous",
  2515. /**
  2516. * Class name assigned next indicators.
  2517. *
  2518. * @property DataTable.CLASS_NEXT
  2519. * @type String
  2520. * @static
  2521. * @final
  2522. * @default "yui-dt-next"
  2523. */
  2524. CLASS_NEXT : "yui-dt-next",
  2525. /**
  2526. * Class name assigned to first elements.
  2527. *
  2528. * @property DataTable.CLASS_FIRST
  2529. * @type String
  2530. * @static
  2531. * @final
  2532. * @default "yui-dt-first"
  2533. */
  2534. CLASS_FIRST : "yui-dt-first",
  2535. /**
  2536. * Class name assigned to last elements.
  2537. *
  2538. * @property DataTable.CLASS_LAST
  2539. * @type String
  2540. * @static
  2541. * @final
  2542. * @default "yui-dt-last"
  2543. */
  2544. CLASS_LAST : "yui-dt-last",
  2545. /**
  2546. * Class name assigned to even elements.
  2547. *
  2548. * @property DataTable.CLASS_EVEN
  2549. * @type String
  2550. * @static
  2551. * @final
  2552. * @default "yui-dt-even"
  2553. */
  2554. CLASS_EVEN : "yui-dt-even",
  2555. /**
  2556. * Class name assigned to odd elements.
  2557. *
  2558. * @property DataTable.CLASS_ODD
  2559. * @type String
  2560. * @static
  2561. * @final
  2562. * @default "yui-dt-odd"
  2563. */
  2564. CLASS_ODD : "yui-dt-odd",
  2565. /**
  2566. * Class name assigned to selected elements.
  2567. *
  2568. * @property DataTable.CLASS_SELECTED
  2569. * @type String
  2570. * @static
  2571. * @final
  2572. * @default "yui-dt-selected"
  2573. */
  2574. CLASS_SELECTED : "yui-dt-selected",
  2575. /**
  2576. * Class name assigned to highlighted elements.
  2577. *
  2578. * @property DataTable.CLASS_HIGHLIGHTED
  2579. * @type String
  2580. * @static
  2581. * @final
  2582. * @default "yui-dt-highlighted"
  2583. */
  2584. CLASS_HIGHLIGHTED : "yui-dt-highlighted",
  2585. /**
  2586. * Class name assigned to hidden elements.
  2587. *
  2588. * @property DataTable.CLASS_HIDDEN
  2589. * @type String
  2590. * @static
  2591. * @final
  2592. * @default "yui-dt-hidden"
  2593. */
  2594. CLASS_HIDDEN : "yui-dt-hidden",
  2595. /**
  2596. * Class name assigned to disabled elements.
  2597. *
  2598. * @property DataTable.CLASS_DISABLED
  2599. * @type String
  2600. * @static
  2601. * @final
  2602. * @default "yui-dt-disabled"
  2603. */
  2604. CLASS_DISABLED : "yui-dt-disabled",
  2605. /**
  2606. * Class name assigned to empty indicators.
  2607. *
  2608. * @property DataTable.CLASS_EMPTY
  2609. * @type String
  2610. * @static
  2611. * @final
  2612. * @default "yui-dt-empty"
  2613. */
  2614. CLASS_EMPTY : "yui-dt-empty",
  2615. /**
  2616. * Class name assigned to loading indicatorx.
  2617. *
  2618. * @property DataTable.CLASS_LOADING
  2619. * @type String
  2620. * @static
  2621. * @final
  2622. * @default "yui-dt-loading"
  2623. */
  2624. CLASS_LOADING : "yui-dt-loading",
  2625. /**
  2626. * Class name assigned to error indicators.
  2627. *
  2628. * @property DataTable.CLASS_ERROR
  2629. * @type String
  2630. * @static
  2631. * @final
  2632. * @default "yui-dt-error"
  2633. */
  2634. CLASS_ERROR : "yui-dt-error",
  2635. /**
  2636. * Class name assigned to editable elements.
  2637. *
  2638. * @property DataTable.CLASS_EDITABLE
  2639. * @type String
  2640. * @static
  2641. * @final
  2642. * @default "yui-dt-editable"
  2643. */
  2644. CLASS_EDITABLE : "yui-dt-editable",
  2645. /**
  2646. * Class name assigned to draggable elements.
  2647. *
  2648. * @property DataTable.CLASS_DRAGGABLE
  2649. * @type String
  2650. * @static
  2651. * @final
  2652. * @default "yui-dt-draggable"
  2653. */
  2654. CLASS_DRAGGABLE : "yui-dt-draggable",
  2655. /**
  2656. * Class name assigned to resizeable elements.
  2657. *
  2658. * @property DataTable.CLASS_RESIZEABLE
  2659. * @type String
  2660. * @static
  2661. * @final
  2662. * @default "yui-dt-resizeable"
  2663. */
  2664. CLASS_RESIZEABLE : "yui-dt-resizeable",
  2665. /**
  2666. * Class name assigned to scrollable elements.
  2667. *
  2668. * @property DataTable.CLASS_SCROLLABLE
  2669. * @type String
  2670. * @static
  2671. * @final
  2672. * @default "yui-dt-scrollable"
  2673. */
  2674. CLASS_SCROLLABLE : "yui-dt-scrollable",
  2675. /**
  2676. * Class name assigned to sortable elements.
  2677. *
  2678. * @property DataTable.CLASS_SORTABLE
  2679. * @type String
  2680. * @static
  2681. * @final
  2682. * @default "yui-dt-sortable"
  2683. */
  2684. CLASS_SORTABLE : "yui-dt-sortable",
  2685. /**
  2686. * Class name assigned to ascending elements.
  2687. *
  2688. * @property DataTable.CLASS_ASC
  2689. * @type String
  2690. * @static
  2691. * @final
  2692. * @default "yui-dt-asc"
  2693. */
  2694. CLASS_ASC : "yui-dt-asc",
  2695. /**
  2696. * Class name assigned to descending elements.
  2697. *
  2698. * @property DataTable.CLASS_DESC
  2699. * @type String
  2700. * @static
  2701. * @final
  2702. * @default "yui-dt-desc"
  2703. */
  2704. CLASS_DESC : "yui-dt-desc",
  2705. /**
  2706. * Class name assigned to BUTTON elements and/or container elements.
  2707. *
  2708. * @property DataTable.CLASS_BUTTON
  2709. * @type String
  2710. * @static
  2711. * @final
  2712. * @default "yui-dt-button"
  2713. */
  2714. CLASS_BUTTON : "yui-dt-button",
  2715. /**
  2716. * Class name assigned to INPUT TYPE=CHECKBOX elements and/or container elements.
  2717. *
  2718. * @property DataTable.CLASS_CHECKBOX
  2719. * @type String
  2720. * @static
  2721. * @final
  2722. * @default "yui-dt-checkbox"
  2723. */
  2724. CLASS_CHECKBOX : "yui-dt-checkbox",
  2725. /**
  2726. * Class name assigned to SELECT elements and/or container elements.
  2727. *
  2728. * @property DataTable.CLASS_DROPDOWN
  2729. * @type String
  2730. * @static
  2731. * @final
  2732. * @default "yui-dt-dropdown"
  2733. */
  2734. CLASS_DROPDOWN : "yui-dt-dropdown",
  2735. /**
  2736. * Class name assigned to INPUT TYPE=RADIO elements and/or container elements.
  2737. *
  2738. * @property DataTable.CLASS_RADIO
  2739. * @type String
  2740. * @static
  2741. * @final
  2742. * @default "yui-dt-radio"
  2743. */
  2744. CLASS_RADIO : "yui-dt-radio",
  2745. /////////////////////////////////////////////////////////////////////////
  2746. //
  2747. // Private static properties
  2748. //
  2749. /////////////////////////////////////////////////////////////////////////
  2750. /**
  2751. * Internal class variable for indexing multiple DataTable instances.
  2752. *
  2753. * @property DataTable._nCount
  2754. * @type Number
  2755. * @private
  2756. * @static
  2757. */
  2758. _nCount : 0,
  2759. /**
  2760. * Internal class variable tracking current number of DataTable instances,
  2761. * so that certain class values can be reset when all instances are destroyed.
  2762. *
  2763. * @property DataTable._nCurrentCount
  2764. * @type Number
  2765. * @private
  2766. * @static
  2767. */
  2768. _nCurrentCount : 0,
  2769. /**
  2770. * Reference to the STYLE node that is dynamically created and updated
  2771. * in order to manage Column widths.
  2772. *
  2773. * @property DataTable._elDynStyleNode
  2774. * @type HTMLElement
  2775. * @private
  2776. * @static
  2777. */
  2778. _elDynStyleNode : null,
  2779. /**
  2780. * Set to true if _elDynStyleNode cannot be populated due to browser incompatibility.
  2781. *
  2782. * @property DataTable._bDynStylesFallback
  2783. * @type boolean
  2784. * @private
  2785. * @static
  2786. */
  2787. _bDynStylesFallback : (ua.ie) ? true : false,
  2788. /**
  2789. * Object literal hash of Columns and their dynamically create style rules.
  2790. *
  2791. * @property DataTable._oDynStyles
  2792. * @type Object
  2793. * @private
  2794. * @static
  2795. */
  2796. _oDynStyles : {},
  2797. /**
  2798. * Element reference to shared Column drag target.
  2799. *
  2800. * @property DataTable._elColumnDragTarget
  2801. * @type HTMLElement
  2802. * @private
  2803. * @static
  2804. */
  2805. _elColumnDragTarget : null,
  2806. /**
  2807. * Element reference to shared Column resizer proxy.
  2808. *
  2809. * @property DataTable._elColumnResizerProxy
  2810. * @type HTMLElement
  2811. * @private
  2812. * @static
  2813. */
  2814. _elColumnResizerProxy : null,
  2815. /////////////////////////////////////////////////////////////////////////
  2816. //
  2817. // Private static methods
  2818. //
  2819. /////////////////////////////////////////////////////////////////////////
  2820. /**
  2821. * Clones object literal or array of object literals.
  2822. *
  2823. * @method DataTable._cloneObject
  2824. * @param o {Object} Object.
  2825. * @private
  2826. * @static
  2827. */
  2828. _cloneObject : function(o) {
  2829. if(!lang.isValue(o)) {
  2830. return o;
  2831. }
  2832. var copy = {};
  2833. if(o instanceof YAHOO.widget.BaseCellEditor) {
  2834. copy = o;
  2835. }
  2836. else if(lang.isFunction(o)) {
  2837. copy = o;
  2838. }
  2839. else if(lang.isArray(o)) {
  2840. var array = [];
  2841. for(var i=0,len=o.length;i<len;i++) {
  2842. array[i] = DT._cloneObject(o[i]);
  2843. }
  2844. copy = array;
  2845. }
  2846. else if(lang.isObject(o)) {
  2847. for (var x in o){
  2848. if(lang.hasOwnProperty(o, x)) {
  2849. if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) {
  2850. copy[x] = DT._cloneObject(o[x]);
  2851. }
  2852. else {
  2853. copy[x] = o[x];
  2854. }
  2855. }
  2856. }
  2857. }
  2858. else {
  2859. copy = o;
  2860. }
  2861. return copy;
  2862. },
  2863. /**
  2864. * Destroys shared Column drag target.
  2865. *
  2866. * @method DataTable._destroyColumnDragTargetEl
  2867. * @private
  2868. * @static
  2869. */
  2870. _destroyColumnDragTargetEl : function() {
  2871. if(DT._elColumnDragTarget) {
  2872. var el = DT._elColumnDragTarget;
  2873. YAHOO.util.Event.purgeElement(el);
  2874. el.parentNode.removeChild(el);
  2875. DT._elColumnDragTarget = null;
  2876. }
  2877. },
  2878. /**
  2879. * Creates HTML markup for shared Column drag target.
  2880. *
  2881. * @method DataTable._initColumnDragTargetEl
  2882. * @return {HTMLElement} Reference to Column drag target.
  2883. * @private
  2884. * @static
  2885. */
  2886. _initColumnDragTargetEl : function() {
  2887. if(!DT._elColumnDragTarget) {
  2888. // Attach Column drag target element as first child of body
  2889. var elColumnDragTarget = document.createElement('div');
  2890. elColumnDragTarget.className = DT.CLASS_COLTARGET;
  2891. elColumnDragTarget.style.display = "none";
  2892. document.body.insertBefore(elColumnDragTarget, document.body.firstChild);
  2893. // Internal tracker of Column drag target
  2894. DT._elColumnDragTarget = elColumnDragTarget;
  2895. }
  2896. return DT._elColumnDragTarget;
  2897. },
  2898. /**
  2899. * Destroys shared Column resizer proxy.
  2900. *
  2901. * @method DataTable._destroyColumnResizerProxyEl
  2902. * @return {HTMLElement} Reference to Column resizer proxy.
  2903. * @private
  2904. * @static
  2905. */
  2906. _destroyColumnResizerProxyEl : function() {
  2907. if(DT._elColumnResizerProxy) {
  2908. var el = DT._elColumnResizerProxy;
  2909. YAHOO.util.Event.purgeElement(el);
  2910. el.parentNode.removeChild(el);
  2911. DT._elColumnResizerProxy = null;
  2912. }
  2913. },
  2914. /**
  2915. * Creates HTML markup for shared Column resizer proxy.
  2916. *
  2917. * @method DataTable._initColumnResizerProxyEl
  2918. * @return {HTMLElement} Reference to Column resizer proxy.
  2919. * @private
  2920. * @static
  2921. */
  2922. _initColumnResizerProxyEl : function() {
  2923. if(!DT._elColumnResizerProxy) {
  2924. // Attach Column resizer element as first child of body
  2925. var elColumnResizerProxy = document.createElement("div");
  2926. elColumnResizerProxy.id = "yui-dt-colresizerproxy"; // Needed for ColumnResizer
  2927. elColumnResizerProxy.className = DT.CLASS_RESIZERPROXY;
  2928. document.body.insertBefore(elColumnResizerProxy, document.body.firstChild);
  2929. // Internal tracker of Column resizer proxy
  2930. DT._elColumnResizerProxy = elColumnResizerProxy;
  2931. }
  2932. return DT._elColumnResizerProxy;
  2933. },
  2934. /**
  2935. * Formats a BUTTON element.
  2936. *
  2937. * @method DataTable.formatButton
  2938. * @param el {HTMLElement} The element to format with markup.
  2939. * @param oRecord {YAHOO.widget.Record} Record instance.
  2940. * @param oColumn {YAHOO.widget.Column} Column instance.
  2941. * @param oData {Object | Boolean} Data value for the cell. By default, the value
  2942. * is what gets written to the BUTTON.
  2943. * @static
  2944. */
  2945. formatButton : function(el, oRecord, oColumn, oData) {
  2946. var sValue = lang.isValue(oData) ? oData : "Click";
  2947. //TODO: support YAHOO.widget.Button
  2948. //if(YAHOO.widget.Button) {
  2949. //}
  2950. //else {
  2951. el.innerHTML = "<button type=\"button\" class=\""+
  2952. DT.CLASS_BUTTON + "\">" + sValue + "</button>";
  2953. //}
  2954. },
  2955. /**
  2956. * Formats a CHECKBOX element.
  2957. *
  2958. * @method DataTable.formatCheckbox
  2959. * @param el {HTMLElement} The element to format with markup.
  2960. * @param oRecord {YAHOO.widget.Record} Record instance.
  2961. * @param oColumn {YAHOO.widget.Column} Column instance.
  2962. * @param oData {Object | Boolean} Data value for the cell. Can be a simple
  2963. * Boolean to indicate whether checkbox is checked or not. Can be object literal
  2964. * {checked:bBoolean, label:sLabel}. Other forms of oData require a custom
  2965. * formatter.
  2966. * @static
  2967. */
  2968. formatCheckbox : function(el, oRecord, oColumn, oData) {
  2969. var bChecked = oData;
  2970. bChecked = (bChecked) ? " checked=\"checked\"" : "";
  2971. el.innerHTML = "<input type=\"checkbox\"" + bChecked +
  2972. " class=\"" + DT.CLASS_CHECKBOX + "\" />";
  2973. },
  2974. /**
  2975. * Formats currency. Default unit is USD.
  2976. *
  2977. * @method DataTable.formatCurrency
  2978. * @param el {HTMLElement} The element to format with markup.
  2979. * @param oRecord {YAHOO.widget.Record} Record instance.
  2980. * @param oColumn {YAHOO.widget.Column} Column instance.
  2981. * @param oData {Number} Data value for the cell.
  2982. * @static
  2983. */
  2984. formatCurrency : function(el, oRecord, oColumn, oData) {
  2985. el.innerHTML = util.Number.format(oData, oColumn.currencyOptions || this.get("currencyOptions"));
  2986. },
  2987. /**
  2988. * Formats JavaScript Dates.
  2989. *
  2990. * @method DataTable.formatDate
  2991. * @param el {HTMLElement} The element to format with markup.
  2992. * @param oRecord {YAHOO.widget.Record} Record instance.
  2993. * @param oColumn {YAHOO.widget.Column} Column instance.
  2994. * @param oData {Object} Data value for the cell, or null.
  2995. * @static
  2996. */
  2997. formatDate : function(el, oRecord, oColumn, oData) {
  2998. var oConfig = oColumn.dateOptions || this.get("dateOptions");
  2999. el.innerHTML = util.Date.format(oData, oConfig, oConfig.locale);
  3000. },
  3001. /**
  3002. * Formats SELECT elements.
  3003. *
  3004. * @method DataTable.formatDropdown
  3005. * @param el {HTMLElement} The element to format with markup.
  3006. * @param oRecord {YAHOO.widget.Record} Record instance.
  3007. * @param oColumn {YAHOO.widget.Column} Column instance.
  3008. * @param oData {Object} Data value for the cell, or null.
  3009. * @static
  3010. */
  3011. formatDropdown : function(el, oRecord, oColumn, oData) {
  3012. var selectedValue = (lang.isValue(oData)) ? oData : oRecord.getData(oColumn.field),
  3013. options = (lang.isArray(oColumn.dropdownOptions)) ?
  3014. oColumn.dropdownOptions : null,
  3015. selectEl,
  3016. collection = el.getElementsByTagName("select");
  3017. // Create the form element only once, so we can attach the onChange listener
  3018. if(collection.length === 0) {
  3019. // Create SELECT element
  3020. selectEl = document.createElement("select");
  3021. selectEl.className = DT.CLASS_DROPDOWN;
  3022. selectEl = el.appendChild(selectEl);
  3023. // Add event listener
  3024. Ev.addListener(selectEl,"change",this._onDropdownChange,this);
  3025. }
  3026. selectEl = collection[0];
  3027. // Update the form element
  3028. if(selectEl) {
  3029. // Clear out previous options
  3030. selectEl.innerHTML = "";
  3031. // We have options to populate
  3032. if(options) {
  3033. // Create OPTION elements
  3034. for(var i=0; i<options.length; i++) {
  3035. var option = options[i];
  3036. var optionEl = document.createElement("option");
  3037. optionEl.value = (lang.isValue(option.value)) ?
  3038. option.value : option;
  3039. // Bug 2334323: Support legacy text, support label for consistency with DropdownCellEditor
  3040. optionEl.innerHTML = (lang.isValue(option.text)) ?
  3041. option.text : (lang.isValue(option.label)) ? option.label : option;
  3042. optionEl = selectEl.appendChild(optionEl);
  3043. if (optionEl.value == selectedValue) {
  3044. optionEl.selected = true;
  3045. }
  3046. }
  3047. }
  3048. // Selected value is our only option
  3049. else {
  3050. selectEl.innerHTML = "<option selected value=\"" + selectedValue + "\">" + selectedValue + "</option>";
  3051. }
  3052. }
  3053. else {
  3054. el.innerHTML = lang.isValue(oData) ? oData : "";
  3055. }
  3056. },
  3057. /**
  3058. * Formats emails.
  3059. *
  3060. * @method DataTable.formatEmail
  3061. * @param el {HTMLElement} The element to format with markup.
  3062. * @param oRecord {YAHOO.widget.Record} Record instance.
  3063. * @param oColumn {YAHOO.widget.Column} Column instance.
  3064. * @param oData {Object} Data value for the cell, or null.
  3065. * @static
  3066. */
  3067. formatEmail : function(el, oRecord, oColumn, oData) {
  3068. if(lang.isString(oData)) {
  3069. el.innerHTML = "<a href=\"mailto:" + oData + "\">" + oData + "</a>";
  3070. }
  3071. else {
  3072. el.innerHTML = lang.isValue(oData) ? oData : "";
  3073. }
  3074. },
  3075. /**
  3076. * Formats links.
  3077. *
  3078. * @method DataTable.formatLink
  3079. * @param el {HTMLElement} The element to format with markup.
  3080. * @param oRecord {YAHOO.widget.Record} Record instance.
  3081. * @param oColumn {YAHOO.widget.Column} Column instance.
  3082. * @param oData {Object} Data value for the cell, or null.
  3083. * @static
  3084. */
  3085. formatLink : function(el, oRecord, oColumn, oData) {
  3086. if(lang.isString(oData)) {
  3087. el.innerHTML = "<a href=\"" + oData + "\">" + oData + "</a>";
  3088. }
  3089. else {
  3090. el.innerHTML = lang.isValue(oData) ? oData : "";
  3091. }
  3092. },
  3093. /**
  3094. * Formats numbers.
  3095. *
  3096. * @method DataTable.formatNumber
  3097. * @param el {HTMLElement} The element to format with markup.
  3098. * @param oRecord {YAHOO.widget.Record} Record instance.
  3099. * @param oColumn {YAHOO.widget.Column} Column instance.
  3100. * @param oData {Object} Data value for the cell, or null.
  3101. * @static
  3102. */
  3103. formatNumber : function(el, oRecord, oColumn, oData) {
  3104. el.innerHTML = util.Number.format(oData, oColumn.numberOptions || this.get("numberOptions"));
  3105. },
  3106. /**
  3107. * Formats INPUT TYPE=RADIO elements.
  3108. *
  3109. * @method DataTable.formatRadio
  3110. * @param el {HTMLElement} The element to format with markup.
  3111. * @param oRecord {YAHOO.widget.Record} Record instance.
  3112. * @param oColumn {YAHOO.widget.Column} Column instance.
  3113. * @param oData {Object} (Optional) Data value for the cell.
  3114. * @static
  3115. */
  3116. formatRadio : function(el, oRecord, oColumn, oData) {
  3117. var bChecked = oData;
  3118. bChecked = (bChecked) ? " checked=\"checked\"" : "";
  3119. el.innerHTML = "<input type=\"radio\"" + bChecked +
  3120. " name=\""+this.getId()+"-col-" + oColumn.getSanitizedKey() + "\"" +
  3121. " class=\"" + DT.CLASS_RADIO+ "\" />";
  3122. },
  3123. /**
  3124. * Formats text strings.
  3125. *
  3126. * @method DataTable.formatText
  3127. * @param el {HTMLElement} The element to format with markup.
  3128. * @param oRecord {YAHOO.widget.Record} Record instance.
  3129. * @param oColumn {YAHOO.widget.Column} Column instance.
  3130. * @param oData {Object} (Optional) Data value for the cell.
  3131. * @static
  3132. */
  3133. formatText : function(el, oRecord, oColumn, oData) {
  3134. var value = (lang.isValue(oData)) ? oData : "";
  3135. //TODO: move to util function
  3136. el.innerHTML = value.toString().replace(/&/g, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;");
  3137. },
  3138. /**
  3139. * Formats TEXTAREA elements.
  3140. *
  3141. * @method DataTable.formatTextarea
  3142. * @param el {HTMLElement} The element to format with markup.
  3143. * @param oRecord {YAHOO.widget.Record} Record instance.
  3144. * @param oColumn {YAHOO.widget.Column} Column instance.
  3145. * @param oData {Object} (Optional) Data value for the cell.
  3146. * @static
  3147. */
  3148. formatTextarea : function(el, oRecord, oColumn, oData) {
  3149. var value = (lang.isValue(oData)) ? oData : "",
  3150. markup = "<textarea>" + value + "</textarea>";
  3151. el.innerHTML = markup;
  3152. },
  3153. /**
  3154. * Formats INPUT TYPE=TEXT elements.
  3155. *
  3156. * @method DataTable.formatTextbox
  3157. * @param el {HTMLElement} The element to format with markup.
  3158. * @param oRecord {YAHOO.widget.Record} Record instance.
  3159. * @param oColumn {YAHOO.widget.Column} Column instance.
  3160. * @param oData {Object} (Optional) Data value for the cell.
  3161. * @static
  3162. */
  3163. formatTextbox : function(el, oRecord, oColumn, oData) {
  3164. var value = (lang.isValue(oData)) ? oData : "",
  3165. markup = "<input type=\"text\" value=\"" + value + "\" />";
  3166. el.innerHTML = markup;
  3167. },
  3168. /**
  3169. * Default cell formatter
  3170. *
  3171. * @method DataTable.formatDefault
  3172. * @param el {HTMLElement} The element to format with markup.
  3173. * @param oRecord {YAHOO.widget.Record} Record instance.
  3174. * @param oColumn {YAHOO.widget.Column} Column instance.
  3175. * @param oData {Object} (Optional) Data value for the cell.
  3176. * @static
  3177. */
  3178. formatDefault : function(el, oRecord, oColumn, oData) {
  3179. el.innerHTML = oData === undefined ||
  3180. oData === null ||
  3181. (typeof oData === 'number' && isNaN(oData)) ?
  3182. "&#160;" : oData.toString();
  3183. },
  3184. /**
  3185. * Validates data value to type Number, doing type conversion as
  3186. * necessary. A valid Number value is return, else null is returned
  3187. * if input value does not validate.
  3188. *
  3189. *
  3190. * @method DataTable.validateNumber
  3191. * @param oData {Object} Data to validate.
  3192. * @static
  3193. */
  3194. validateNumber : function(oData) {
  3195. //Convert to number
  3196. var number = oData * 1;
  3197. // Validate
  3198. if(lang.isNumber(number)) {
  3199. return number;
  3200. }
  3201. else {
  3202. YAHOO.log("Could not validate data " + lang.dump(oData) + " to type Number", "warn", this.toString());
  3203. return undefined;
  3204. }
  3205. }
  3206. });
  3207. // Done in separate step so referenced functions are defined.
  3208. /**
  3209. * Cell formatting functions.
  3210. * @property DataTable.Formatter
  3211. * @type Object
  3212. * @static
  3213. */
  3214. DT.Formatter = {
  3215. button : DT.formatButton,
  3216. checkbox : DT.formatCheckbox,
  3217. currency : DT.formatCurrency,
  3218. "date" : DT.formatDate,
  3219. dropdown : DT.formatDropdown,
  3220. email : DT.formatEmail,
  3221. link : DT.formatLink,
  3222. "number" : DT.formatNumber,
  3223. radio : DT.formatRadio,
  3224. text : DT.formatText,
  3225. textarea : DT.formatTextarea,
  3226. textbox : DT.formatTextbox,
  3227. defaultFormatter : DT.formatDefault
  3228. };
  3229. lang.extend(DT, util.Element, {
  3230. /////////////////////////////////////////////////////////////////////////////
  3231. //
  3232. // Superclass methods
  3233. //
  3234. /////////////////////////////////////////////////////////////////////////////
  3235. /**
  3236. * Implementation of Element's abstract method. Sets up config values.
  3237. *
  3238. * @method initAttributes
  3239. * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
  3240. * @private
  3241. */
  3242. initAttributes : function(oConfigs) {
  3243. oConfigs = oConfigs || {};
  3244. DT.superclass.initAttributes.call(this, oConfigs);
  3245. /**
  3246. * @attribute summary
  3247. * @description Value for the SUMMARY attribute.
  3248. * @type String
  3249. * @default ""
  3250. */
  3251. this.setAttributeConfig("summary", {
  3252. value: "",
  3253. validator: lang.isString,
  3254. method: function(sSummary) {
  3255. if(this._elTable) {
  3256. this._elTable.summary = sSummary;
  3257. }
  3258. }
  3259. });
  3260. /**
  3261. * @attribute selectionMode
  3262. * @description Specifies row or cell selection mode. Accepts the following strings:
  3263. * <dl>
  3264. * <dt>"standard"</dt>
  3265. * <dd>Standard row selection with support for modifier keys to enable
  3266. * multiple selections.</dd>
  3267. *
  3268. * <dt>"single"</dt>
  3269. * <dd>Row selection with modifier keys disabled to not allow
  3270. * multiple selections.</dd>
  3271. *
  3272. * <dt>"singlecell"</dt>
  3273. * <dd>Cell selection with modifier keys disabled to not allow
  3274. * multiple selections.</dd>
  3275. *
  3276. * <dt>"cellblock"</dt>
  3277. * <dd>Cell selection with support for modifier keys to enable multiple
  3278. * selections in a block-fashion, like a spreadsheet.</dd>
  3279. *
  3280. * <dt>"cellrange"</dt>
  3281. * <dd>Cell selection with support for modifier keys to enable multiple
  3282. * selections in a range-fashion, like a calendar.</dd>
  3283. * </dl>
  3284. *
  3285. * @default "standard"
  3286. * @type String
  3287. */
  3288. this.setAttributeConfig("selectionMode", {
  3289. value: "standard",
  3290. validator: lang.isString
  3291. });
  3292. /**
  3293. * @attribute sortedBy
  3294. * @description Object literal provides metadata for initial sort values if
  3295. * data will arrive pre-sorted:
  3296. * <dl>
  3297. * <dt>sortedBy.key</dt>
  3298. * <dd>{String} Key of sorted Column</dd>
  3299. * <dt>sortedBy.dir</dt>
  3300. * <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
  3301. * </dl>
  3302. * @type Object | null
  3303. */
  3304. this.setAttributeConfig("sortedBy", {
  3305. value: null,
  3306. // TODO: accepted array for nested sorts
  3307. validator: function(oNewSortedBy) {
  3308. if(oNewSortedBy) {
  3309. return (lang.isObject(oNewSortedBy) && oNewSortedBy.key);
  3310. }
  3311. else {
  3312. return (oNewSortedBy === null);
  3313. }
  3314. },
  3315. method: function(oNewSortedBy) {
  3316. // Stash the previous value
  3317. var oOldSortedBy = this.get("sortedBy");
  3318. // Workaround for bug 1827195
  3319. this._configs.sortedBy.value = oNewSortedBy;
  3320. // Remove ASC/DESC from TH
  3321. var oOldColumn,
  3322. nOldColumnKeyIndex,
  3323. oNewColumn,
  3324. nNewColumnKeyIndex;
  3325. if(this._elThead) {
  3326. if(oOldSortedBy && oOldSortedBy.key && oOldSortedBy.dir) {
  3327. oOldColumn = this._oColumnSet.getColumn(oOldSortedBy.key);
  3328. nOldColumnKeyIndex = oOldColumn.getKeyIndex();
  3329. // Remove previous UI from THEAD
  3330. var elOldTh = oOldColumn.getThEl();
  3331. Dom.removeClass(elOldTh, oOldSortedBy.dir);
  3332. this.formatTheadCell(oOldColumn.getThLinerEl().firstChild, oOldColumn, oNewSortedBy);
  3333. }
  3334. if(oNewSortedBy) {
  3335. oNewColumn = (oNewSortedBy.column) ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key);
  3336. nNewColumnKeyIndex = oNewColumn.getKeyIndex();
  3337. // Update THEAD with new UI
  3338. var elNewTh = oNewColumn.getThEl();
  3339. // Backward compatibility
  3340. if(oNewSortedBy.dir && ((oNewSortedBy.dir == "asc") || (oNewSortedBy.dir == "desc"))) {
  3341. var newClass = (oNewSortedBy.dir == "desc") ?
  3342. DT.CLASS_DESC :
  3343. DT.CLASS_ASC;
  3344. Dom.addClass(elNewTh, newClass);
  3345. }
  3346. else {
  3347. var sortClass = oNewSortedBy.dir || DT.CLASS_ASC;
  3348. Dom.addClass(elNewTh, sortClass);
  3349. }
  3350. this.formatTheadCell(oNewColumn.getThLinerEl().firstChild, oNewColumn, oNewSortedBy);
  3351. }
  3352. }
  3353. if(this._elTbody) {
  3354. // Update TBODY UI
  3355. this._elTbody.style.display = "none";
  3356. var allRows = this._elTbody.rows,
  3357. allCells;
  3358. for(var i=allRows.length-1; i>-1; i--) {
  3359. allCells = allRows[i].childNodes;
  3360. if(allCells[nOldColumnKeyIndex]) {
  3361. Dom.removeClass(allCells[nOldColumnKeyIndex], oOldSortedBy.dir);
  3362. }
  3363. if(allCells[nNewColumnKeyIndex]) {
  3364. Dom.addClass(allCells[nNewColumnKeyIndex], oNewSortedBy.dir);
  3365. }
  3366. }
  3367. this._elTbody.style.display = "";
  3368. }
  3369. this._clearTrTemplateEl();
  3370. }
  3371. });
  3372. /**
  3373. * @attribute paginator
  3374. * @description An instance of YAHOO.widget.Paginator.
  3375. * @default null
  3376. * @type {Object|YAHOO.widget.Paginator}
  3377. */
  3378. this.setAttributeConfig("paginator", {
  3379. value : null,
  3380. validator : function (val) {
  3381. return val === null || val instanceof widget.Paginator;
  3382. },
  3383. method : function () { this._updatePaginator.apply(this,arguments); }
  3384. });
  3385. /**
  3386. * @attribute caption
  3387. * @description Value for the CAPTION element. NB: Not supported in
  3388. * ScrollingDataTable.
  3389. * @type String
  3390. */
  3391. this.setAttributeConfig("caption", {
  3392. value: null,
  3393. validator: lang.isString,
  3394. method: function(sCaption) {
  3395. this._initCaptionEl(sCaption);
  3396. }
  3397. });
  3398. /**
  3399. * @attribute draggableColumns
  3400. * @description True if Columns are draggable to reorder, false otherwise.
  3401. * The Drag & Drop Utility is required to enable this feature. Only top-level
  3402. * and non-nested Columns are draggable. Write once.
  3403. * @default false
  3404. * @type Boolean
  3405. */
  3406. this.setAttributeConfig("draggableColumns", {
  3407. value: false,
  3408. validator: lang.isBoolean,
  3409. method: function(oParam) {
  3410. if(this._elThead) {
  3411. if(oParam) {
  3412. this._initDraggableColumns();
  3413. }
  3414. else {
  3415. this._destroyDraggableColumns();
  3416. }
  3417. }
  3418. }
  3419. });
  3420. /**
  3421. * @attribute renderLoopSize
  3422. * @description A value greater than 0 enables DOM rendering of rows to be
  3423. * executed from a non-blocking timeout queue and sets how many rows to be
  3424. * rendered per timeout. Recommended for very large data sets.
  3425. * @type Number
  3426. * @default 0
  3427. */
  3428. this.setAttributeConfig("renderLoopSize", {
  3429. value: 0,
  3430. validator: lang.isNumber
  3431. });
  3432. /**
  3433. * @attribute formatRow
  3434. * @description A function that accepts a TR element and its associated Record
  3435. * for custom formatting. The function must return TRUE in order to automatically
  3436. * continue formatting of child TD elements, else TD elements will not be
  3437. * automatically formatted.
  3438. * @type function
  3439. * @default null
  3440. */
  3441. this.setAttributeConfig("formatRow", {
  3442. value: null,
  3443. validator: lang.isFunction
  3444. });
  3445. /**
  3446. * @attribute generateRequest
  3447. * @description A function that converts an object literal of desired DataTable
  3448. * states into a request value which is then passed to the DataSource's
  3449. * sendRequest method in order to retrieve data for those states. This
  3450. * function is passed an object literal of state data and a reference to the
  3451. * DataTable instance:
  3452. *
  3453. * <dl>
  3454. * <dt>pagination<dt>
  3455. * <dd>
  3456. * <dt>offsetRecord</dt>
  3457. * <dd>{Number} Index of the first Record of the desired page</dd>
  3458. * <dt>rowsPerPage</dt>
  3459. * <dd>{Number} Number of rows per page</dd>
  3460. * </dd>
  3461. * <dt>sortedBy</dt>
  3462. * <dd>
  3463. * <dt>key</dt>
  3464. * <dd>{String} Key of sorted Column</dd>
  3465. * <dt>dir</dt>
  3466. * <dd>{String} Sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
  3467. * </dd>
  3468. * <dt>self</dt>
  3469. * <dd>The DataTable instance</dd>
  3470. * </dl>
  3471. *
  3472. * and by default returns a String of syntax:
  3473. * "sort={sortColumn}&dir={sortDir}&startIndex={pageStartIndex}&results={rowsPerPage}"
  3474. * @type function
  3475. * @default HTMLFunction
  3476. */
  3477. this.setAttributeConfig("generateRequest", {
  3478. value: function(oState, oSelf) {
  3479. // Set defaults
  3480. oState = oState || {pagination:null, sortedBy:null};
  3481. var sort = encodeURIComponent((oState.sortedBy) ? oState.sortedBy.key : oSelf.getColumnSet().keys[0].getKey());
  3482. var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc";
  3483. var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
  3484. var results = (oState.pagination) ? oState.pagination.rowsPerPage : null;
  3485. // Build the request
  3486. return "sort=" + sort +
  3487. "&dir=" + dir +
  3488. "&startIndex=" + startIndex +
  3489. ((results !== null) ? "&results=" + results : "");
  3490. },
  3491. validator: lang.isFunction
  3492. });
  3493. /**
  3494. * @attribute initialRequest
  3495. * @description Defines the initial request that gets sent to the DataSource
  3496. * during initialization. Value is ignored if initialLoad is set to any value
  3497. * other than true.
  3498. * @type MIXED
  3499. * @default null
  3500. */
  3501. this.setAttributeConfig("initialRequest", {
  3502. value: null
  3503. });
  3504. /**
  3505. * @attribute initialLoad
  3506. * @description Determines whether or not to load data at instantiation. By
  3507. * default, will trigger a sendRequest() to the DataSource and pass in the
  3508. * request defined by initialRequest. If set to false, data will not load
  3509. * at instantiation. Alternatively, implementers who wish to work with a
  3510. * custom payload may pass in an object literal with the following values:
  3511. *
  3512. * <dl>
  3513. * <dt>request (MIXED)</dt>
  3514. * <dd>Request value.</dd>
  3515. *
  3516. * <dt>argument (MIXED)</dt>
  3517. * <dd>Custom data that will be passed through to the callback function.</dd>
  3518. * </dl>
  3519. *
  3520. *
  3521. * @type Boolean | Object
  3522. * @default true
  3523. */
  3524. this.setAttributeConfig("initialLoad", {
  3525. value: true
  3526. });
  3527. /**
  3528. * @attribute dynamicData
  3529. * @description If true, sorting and pagination are relegated to the DataSource
  3530. * for handling, using the request returned by the "generateRequest" function.
  3531. * Each new DataSource response blows away all previous Records. False by default, so
  3532. * sorting and pagination will be handled directly on the client side, without
  3533. * causing any new requests for data from the DataSource.
  3534. * @type Boolean
  3535. * @default false
  3536. */
  3537. this.setAttributeConfig("dynamicData", {
  3538. value: false,
  3539. validator: lang.isBoolean
  3540. });
  3541. /**
  3542. * @attribute MSG_EMPTY
  3543. * @description Message to display if DataTable has no data.
  3544. * @type String
  3545. * @default "No records found."
  3546. */
  3547. this.setAttributeConfig("MSG_EMPTY", {
  3548. value: "No records found.",
  3549. validator: lang.isString
  3550. });
  3551. /**
  3552. * @attribute MSG_LOADING
  3553. * @description Message to display while DataTable is loading data.
  3554. * @type String
  3555. * @default "Loading..."
  3556. */
  3557. this.setAttributeConfig("MSG_LOADING", {
  3558. value: "Loading...",
  3559. validator: lang.isString
  3560. });
  3561. /**
  3562. * @attribute MSG_ERROR
  3563. * @description Message to display while DataTable has data error.
  3564. * @type String
  3565. * @default "Data error."
  3566. */
  3567. this.setAttributeConfig("MSG_ERROR", {
  3568. value: "Data error.",
  3569. validator: lang.isString
  3570. });
  3571. /**
  3572. * @attribute MSG_SORTASC
  3573. * @description Message to display in tooltip to sort Column in ascending order.
  3574. * @type String
  3575. * @default "Click to sort ascending"
  3576. */
  3577. this.setAttributeConfig("MSG_SORTASC", {
  3578. value: "Click to sort ascending",
  3579. validator: lang.isString,
  3580. method: function(sParam) {
  3581. if(this._elThead) {
  3582. for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
  3583. if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_ASC) {
  3584. allKeys[i]._elThLabel.firstChild.title = sParam;
  3585. }
  3586. }
  3587. }
  3588. }
  3589. });
  3590. /**
  3591. * @attribute MSG_SORTDESC
  3592. * @description Message to display in tooltip to sort Column in descending order.
  3593. * @type String
  3594. * @default "Click to sort descending"
  3595. */
  3596. this.setAttributeConfig("MSG_SORTDESC", {
  3597. value: "Click to sort descending",
  3598. validator: lang.isString,
  3599. method: function(sParam) {
  3600. if(this._elThead) {
  3601. for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
  3602. if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_DESC) {
  3603. allKeys[i]._elThLabel.firstChild.title = sParam;
  3604. }
  3605. }
  3606. }
  3607. }
  3608. });
  3609. /**
  3610. * @attribute currencySymbol
  3611. * @deprecated
  3612. */
  3613. this.setAttributeConfig("currencySymbol", {
  3614. value: "$",
  3615. validator: lang.isString
  3616. });
  3617. /**
  3618. * Default config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
  3619. * @attribute currencyOptions
  3620. * @type Object
  3621. * @default {prefix: $, decimalPlaces:2, decimalSeparator:".", thousandsSeparator:","}
  3622. */
  3623. this.setAttributeConfig("currencyOptions", {
  3624. value: {
  3625. prefix: this.get("currencySymbol"), // TODO: deprecate currencySymbol
  3626. decimalPlaces:2,
  3627. decimalSeparator:".",
  3628. thousandsSeparator:","
  3629. }
  3630. });
  3631. /**
  3632. * Default config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
  3633. * @attribute dateOptions
  3634. * @type Object
  3635. * @default {format:"%m/%d/%Y", locale:"en"}
  3636. */
  3637. this.setAttributeConfig("dateOptions", {
  3638. value: {format:"%m/%d/%Y", locale:"en"}
  3639. });
  3640. /**
  3641. * Default config passed to YAHOO.util.Number.format() by the 'number' Column formatter.
  3642. * @attribute numberOptions
  3643. * @type Object
  3644. * @default {decimalPlaces:0, thousandsSeparator:","}
  3645. */
  3646. this.setAttributeConfig("numberOptions", {
  3647. value: {
  3648. decimalPlaces:0,
  3649. thousandsSeparator:","
  3650. }
  3651. });
  3652. },
  3653. /////////////////////////////////////////////////////////////////////////////
  3654. //
  3655. // Private member variables
  3656. //
  3657. /////////////////////////////////////////////////////////////////////////////
  3658. /**
  3659. * True if instance is initialized, so as to fire the initEvent after render.
  3660. *
  3661. * @property _bInit
  3662. * @type Boolean
  3663. * @default true
  3664. * @private
  3665. */
  3666. _bInit : true,
  3667. /**
  3668. * Index assigned to instance.
  3669. *
  3670. * @property _nIndex
  3671. * @type Number
  3672. * @private
  3673. */
  3674. _nIndex : null,
  3675. /**
  3676. * Counter for IDs assigned to TR elements.
  3677. *
  3678. * @property _nTrCount
  3679. * @type Number
  3680. * @private
  3681. */
  3682. _nTrCount : 0,
  3683. /**
  3684. * Counter for IDs assigned to TD elements.
  3685. *
  3686. * @property _nTdCount
  3687. * @type Number
  3688. * @private
  3689. */
  3690. _nTdCount : 0,
  3691. /**
  3692. * Unique id assigned to instance "yui-dtN", useful prefix for generating unique
  3693. * DOM ID strings and log messages.
  3694. *
  3695. * @property _sId
  3696. * @type String
  3697. * @private
  3698. */
  3699. _sId : null,
  3700. /**
  3701. * Render chain.
  3702. *
  3703. * @property _oChainRender
  3704. * @type YAHOO.util.Chain
  3705. * @private
  3706. */
  3707. _oChainRender : null,
  3708. /**
  3709. * DOM reference to the container element for the DataTable instance into which
  3710. * all other elements get created.
  3711. *
  3712. * @property _elContainer
  3713. * @type HTMLElement
  3714. * @private
  3715. */
  3716. _elContainer : null,
  3717. /**
  3718. * DOM reference to the mask element for the DataTable instance which disables it.
  3719. *
  3720. * @property _elMask
  3721. * @type HTMLElement
  3722. * @private
  3723. */
  3724. _elMask : null,
  3725. /**
  3726. * DOM reference to the TABLE element for the DataTable instance.
  3727. *
  3728. * @property _elTable
  3729. * @type HTMLElement
  3730. * @private
  3731. */
  3732. _elTable : null,
  3733. /**
  3734. * DOM reference to the CAPTION element for the DataTable instance.
  3735. *
  3736. * @property _elCaption
  3737. * @type HTMLElement
  3738. * @private
  3739. */
  3740. _elCaption : null,
  3741. /**
  3742. * DOM reference to the COLGROUP element for the DataTable instance.
  3743. *
  3744. * @property _elColgroup
  3745. * @type HTMLElement
  3746. * @private
  3747. */
  3748. _elColgroup : null,
  3749. /**
  3750. * DOM reference to the THEAD element for the DataTable instance.
  3751. *
  3752. * @property _elThead
  3753. * @type HTMLElement
  3754. * @private
  3755. */
  3756. _elThead : null,
  3757. /**
  3758. * DOM reference to the primary TBODY element for the DataTable instance.
  3759. *
  3760. * @property _elTbody
  3761. * @type HTMLElement
  3762. * @private
  3763. */
  3764. _elTbody : null,
  3765. /**
  3766. * DOM reference to the secondary TBODY element used to display DataTable messages.
  3767. *
  3768. * @property _elMsgTbody
  3769. * @type HTMLElement
  3770. * @private
  3771. */
  3772. _elMsgTbody : null,
  3773. /**
  3774. * DOM reference to the secondary TBODY element's single TR element used to display DataTable messages.
  3775. *
  3776. * @property _elMsgTr
  3777. * @type HTMLElement
  3778. * @private
  3779. */
  3780. _elMsgTr : null,
  3781. /**
  3782. * DOM reference to the secondary TBODY element's single TD element used to display DataTable messages.
  3783. *
  3784. * @property _elMsgTd
  3785. * @type HTMLElement
  3786. * @private
  3787. */
  3788. _elMsgTd : null,
  3789. /**
  3790. * DataSource instance for the DataTable instance.
  3791. *
  3792. * @property _oDataSource
  3793. * @type YAHOO.util.DataSource
  3794. * @private
  3795. */
  3796. _oDataSource : null,
  3797. /**
  3798. * ColumnSet instance for the DataTable instance.
  3799. *
  3800. * @property _oColumnSet
  3801. * @type YAHOO.widget.ColumnSet
  3802. * @private
  3803. */
  3804. _oColumnSet : null,
  3805. /**
  3806. * RecordSet instance for the DataTable instance.
  3807. *
  3808. * @property _oRecordSet
  3809. * @type YAHOO.widget.RecordSet
  3810. * @private
  3811. */
  3812. _oRecordSet : null,
  3813. /**
  3814. * The active CellEditor instance for the DataTable instance.
  3815. *
  3816. * @property _oCellEditor
  3817. * @type YAHOO.widget.CellEditor
  3818. * @private
  3819. */
  3820. _oCellEditor : null,
  3821. /**
  3822. * ID string of first TR element of the current DataTable page.
  3823. *
  3824. * @property _sFirstTrId
  3825. * @type String
  3826. * @private
  3827. */
  3828. _sFirstTrId : null,
  3829. /**
  3830. * ID string of the last TR element of the current DataTable page.
  3831. *
  3832. * @property _sLastTrId
  3833. * @type String
  3834. * @private
  3835. */
  3836. _sLastTrId : null,
  3837. /**
  3838. * Template row to create all new rows from.
  3839. * @property _elTrTemplate
  3840. * @type {HTMLElement}
  3841. * @private
  3842. */
  3843. _elTrTemplate : null,
  3844. /**
  3845. * Sparse array of custom functions to set column widths for browsers that don't
  3846. * support dynamic CSS rules. Functions are added at the index representing
  3847. * the number of rows they update.
  3848. *
  3849. * @property _aDynFunctions
  3850. * @type Array
  3851. * @private
  3852. */
  3853. _aDynFunctions : [],
  3854. /////////////////////////////////////////////////////////////////////////////
  3855. //
  3856. // Private methods
  3857. //
  3858. /////////////////////////////////////////////////////////////////////////////
  3859. /**
  3860. * Clears browser text selection. Useful to call on rowSelectEvent or
  3861. * cellSelectEvent to prevent clicks or dblclicks from selecting text in the
  3862. * browser.
  3863. *
  3864. * @method clearTextSelection
  3865. */
  3866. clearTextSelection : function() {
  3867. var sel;
  3868. if(window.getSelection) {
  3869. sel = window.getSelection();
  3870. }
  3871. else if(document.getSelection) {
  3872. sel = document.getSelection();
  3873. }
  3874. else if(document.selection) {
  3875. sel = document.selection;
  3876. }
  3877. if(sel) {
  3878. if(sel.empty) {
  3879. sel.empty();
  3880. }
  3881. else if (sel.removeAllRanges) {
  3882. sel.removeAllRanges();
  3883. }
  3884. else if(sel.collapse) {
  3885. sel.collapse();
  3886. }
  3887. }
  3888. },
  3889. /**
  3890. * Sets focus on the given element.
  3891. *
  3892. * @method _focusEl
  3893. * @param el {HTMLElement} Element.
  3894. * @private
  3895. */
  3896. _focusEl : function(el) {
  3897. el = el || this._elTbody;
  3898. // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
  3899. // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
  3900. // strange unexpected things as the user clicks on buttons and other controls.
  3901. setTimeout(function() {
  3902. try {
  3903. el.focus();
  3904. }
  3905. catch(e) {
  3906. }
  3907. },0);
  3908. },
  3909. /**
  3910. * Forces Gecko repaint.
  3911. *
  3912. * @method _repaintGecko
  3913. * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
  3914. * @private
  3915. */
  3916. _repaintGecko : (ua.gecko) ?
  3917. function(el) {
  3918. el = el || this._elContainer;
  3919. var parent = el.parentNode;
  3920. var nextSibling = el.nextSibling;
  3921. parent.insertBefore(parent.removeChild(el), nextSibling);
  3922. } : function() {},
  3923. /**
  3924. * Forces Opera repaint.
  3925. *
  3926. * @method _repaintOpera
  3927. * @private
  3928. */
  3929. _repaintOpera : (ua.opera) ?
  3930. function() {
  3931. if(ua.opera) {
  3932. document.documentElement.className += " ";
  3933. document.documentElement.className = YAHOO.lang.trim(document.documentElement.className);
  3934. }
  3935. } : function() {} ,
  3936. /**
  3937. * Forces Webkit repaint.
  3938. *
  3939. * @method _repaintWebkit
  3940. * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
  3941. * @private
  3942. */
  3943. _repaintWebkit : (ua.webkit) ?
  3944. function(el) {
  3945. el = el || this._elContainer;
  3946. var parent = el.parentNode;
  3947. var nextSibling = el.nextSibling;
  3948. parent.insertBefore(parent.removeChild(el), nextSibling);
  3949. } : function() {},
  3950. // INIT FUNCTIONS
  3951. /**
  3952. * Initializes object literal of config values.
  3953. *
  3954. * @method _initConfigs
  3955. * @param oConfig {Object} Object literal of config values.
  3956. * @private
  3957. */
  3958. _initConfigs : function(oConfigs) {
  3959. if(!oConfigs || !lang.isObject(oConfigs)) {
  3960. oConfigs = {};
  3961. }
  3962. this.configs = oConfigs;
  3963. },
  3964. /**
  3965. * Initializes ColumnSet.
  3966. *
  3967. * @method _initColumnSet
  3968. * @param aColumnDefs {Object[]} Array of object literal Column definitions.
  3969. * @private
  3970. */
  3971. _initColumnSet : function(aColumnDefs) {
  3972. var oColumn, i, len;
  3973. if(this._oColumnSet) {
  3974. // First clear _oDynStyles for existing ColumnSet and
  3975. // uregister CellEditor Custom Events
  3976. for(i=0, len=this._oColumnSet.keys.length; i<len; i++) {
  3977. oColumn = this._oColumnSet.keys[i];
  3978. DT._oDynStyles["."+this.getId()+"-col-"+oColumn.getSanitizedKey()+" ."+DT.CLASS_LINER] = undefined;
  3979. if(oColumn.editor && oColumn.editor.unsubscribeAll) { // Backward compatibility
  3980. oColumn.editor.unsubscribeAll();
  3981. }
  3982. }
  3983. this._oColumnSet = null;
  3984. this._clearTrTemplateEl();
  3985. }
  3986. if(lang.isArray(aColumnDefs)) {
  3987. this._oColumnSet = new YAHOO.widget.ColumnSet(aColumnDefs);
  3988. }
  3989. // Backward compatibility
  3990. else if(aColumnDefs instanceof YAHOO.widget.ColumnSet) {
  3991. this._oColumnSet = aColumnDefs;
  3992. YAHOO.log("DataTable's constructor now requires an array" +
  3993. " of object literal Column definitions instead of a ColumnSet instance",
  3994. "warn", this.toString());
  3995. }
  3996. // Register CellEditor Custom Events
  3997. var allKeys = this._oColumnSet.keys;
  3998. for(i=0, len=allKeys.length; i<len; i++) {
  3999. oColumn = allKeys[i];
  4000. if(oColumn.editor && oColumn.editor.subscribe) { // Backward incompatibility
  4001. oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
  4002. oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
  4003. oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
  4004. oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
  4005. oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
  4006. oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
  4007. oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
  4008. oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
  4009. }
  4010. }
  4011. },
  4012. /**
  4013. * Initializes DataSource.
  4014. *
  4015. * @method _initDataSource
  4016. * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
  4017. * @private
  4018. */
  4019. _initDataSource : function(oDataSource) {
  4020. this._oDataSource = null;
  4021. if(oDataSource && (lang.isFunction(oDataSource.sendRequest))) {
  4022. this._oDataSource = oDataSource;
  4023. }
  4024. // Backward compatibility
  4025. else {
  4026. var tmpTable = null;
  4027. var tmpContainer = this._elContainer;
  4028. var i=0;
  4029. //TODO: this will break if re-initing DS at runtime for SDT
  4030. // Peek in container child nodes to see if TABLE already exists
  4031. if(tmpContainer.hasChildNodes()) {
  4032. var tmpChildren = tmpContainer.childNodes;
  4033. for(i=0; i<tmpChildren.length; i++) {
  4034. if(tmpChildren[i].nodeName && tmpChildren[i].nodeName.toLowerCase() == "table") {
  4035. tmpTable = tmpChildren[i];
  4036. break;
  4037. }
  4038. }
  4039. if(tmpTable) {
  4040. var tmpFieldsArray = [];
  4041. for(; i<this._oColumnSet.keys.length; i++) {
  4042. tmpFieldsArray.push({key:this._oColumnSet.keys[i].key});
  4043. }
  4044. this._oDataSource = new DS(tmpTable);
  4045. this._oDataSource.responseType = DS.TYPE_HTMLTABLE;
  4046. this._oDataSource.responseSchema = {fields: tmpFieldsArray};
  4047. YAHOO.log("Null DataSource for progressive enhancement from" +
  4048. " markup has been deprecated", "warn", this.toString());
  4049. }
  4050. }
  4051. }
  4052. },
  4053. /**
  4054. * Initializes RecordSet.
  4055. *
  4056. * @method _initRecordSet
  4057. * @private
  4058. */
  4059. _initRecordSet : function() {
  4060. if(this._oRecordSet) {
  4061. this._oRecordSet.reset();
  4062. }
  4063. else {
  4064. this._oRecordSet = new YAHOO.widget.RecordSet();
  4065. }
  4066. },
  4067. /**
  4068. * Initializes DOM elements.
  4069. *
  4070. * @method _initDomElements
  4071. * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
  4072. * return {Boolean} False in case of error, otherwise true
  4073. * @private
  4074. */
  4075. _initDomElements : function(elContainer) {
  4076. // Outer container
  4077. this._initContainerEl(elContainer);
  4078. // TABLE
  4079. this._initTableEl(this._elContainer);
  4080. // COLGROUP
  4081. this._initColgroupEl(this._elTable);
  4082. // THEAD
  4083. this._initTheadEl(this._elTable);
  4084. // Message TBODY
  4085. this._initMsgTbodyEl(this._elTable);
  4086. // Primary TBODY
  4087. this._initTbodyEl(this._elTable);
  4088. if(!this._elContainer || !this._elTable || !this._elColgroup || !this._elThead || !this._elTbody || !this._elMsgTbody) {
  4089. return false;
  4090. }
  4091. else {
  4092. return true;
  4093. }
  4094. },
  4095. /**
  4096. * Destroy's the DataTable outer container element, if available.
  4097. *
  4098. * @method _destroyContainerEl
  4099. * @param elContainer {HTMLElement} Reference to the container element.
  4100. * @private
  4101. */
  4102. _destroyContainerEl : function(elContainer) {
  4103. Dom.removeClass(elContainer, DT.CLASS_DATATABLE);
  4104. Ev.purgeElement(elContainer, true);
  4105. elContainer.innerHTML = "";
  4106. this._elContainer = null;
  4107. this._elColgroup = null;
  4108. this._elThead = null;
  4109. this._elTbody = null;
  4110. },
  4111. /**
  4112. * Initializes the DataTable outer container element, including a mask.
  4113. *
  4114. * @method _initContainerEl
  4115. * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
  4116. * @private
  4117. */
  4118. _initContainerEl : function(elContainer) {
  4119. // Validate container
  4120. elContainer = Dom.get(elContainer);
  4121. if(elContainer && elContainer.nodeName && (elContainer.nodeName.toLowerCase() == "div")) {
  4122. // Destroy previous
  4123. this._destroyContainerEl(elContainer);
  4124. Dom.addClass(elContainer, DT.CLASS_DATATABLE);
  4125. Ev.addListener(elContainer, "focus", this._onTableFocus, this);
  4126. Ev.addListener(elContainer, "dblclick", this._onTableDblclick, this);
  4127. this._elContainer = elContainer;
  4128. var elMask = document.createElement("div");
  4129. elMask.className = DT.CLASS_MASK;
  4130. elMask.style.display = "none";
  4131. this._elMask = elContainer.appendChild(elMask);
  4132. }
  4133. },
  4134. /**
  4135. * Destroy's the DataTable TABLE element, if available.
  4136. *
  4137. * @method _destroyTableEl
  4138. * @private
  4139. */
  4140. _destroyTableEl : function() {
  4141. var elTable = this._elTable;
  4142. if(elTable) {
  4143. Ev.purgeElement(elTable, true);
  4144. elTable.parentNode.removeChild(elTable);
  4145. this._elCaption = null;
  4146. this._elColgroup = null;
  4147. this._elThead = null;
  4148. this._elTbody = null;
  4149. }
  4150. },
  4151. /**
  4152. * Creates HTML markup CAPTION element.
  4153. *
  4154. * @method _initCaptionEl
  4155. * @param sCaption {String} Text for caption.
  4156. * @private
  4157. */
  4158. _initCaptionEl : function(sCaption) {
  4159. if(this._elTable && sCaption) {
  4160. // Create CAPTION element
  4161. if(!this._elCaption) {
  4162. this._elCaption = this._elTable.createCaption();
  4163. }
  4164. // Set CAPTION value
  4165. this._elCaption.innerHTML = sCaption;
  4166. }
  4167. else if(this._elCaption) {
  4168. this._elCaption.parentNode.removeChild(this._elCaption);
  4169. }
  4170. },
  4171. /**
  4172. * Creates HTML markup for TABLE, COLGROUP, THEAD and TBODY elements in outer
  4173. * container element.
  4174. *
  4175. * @method _initTableEl
  4176. * @param elContainer {HTMLElement} Container element into which to create TABLE.
  4177. * @private
  4178. */
  4179. _initTableEl : function(elContainer) {
  4180. if(elContainer) {
  4181. // Destroy previous
  4182. this._destroyTableEl();
  4183. // Create TABLE
  4184. this._elTable = elContainer.appendChild(document.createElement("table"));
  4185. // Set SUMMARY attribute
  4186. this._elTable.summary = this.get("summary");
  4187. // Create CAPTION element
  4188. if(this.get("caption")) {
  4189. this._initCaptionEl(this.get("caption"));
  4190. }
  4191. }
  4192. },
  4193. /**
  4194. * Destroy's the DataTable COLGROUP element, if available.
  4195. *
  4196. * @method _destroyColgroupEl
  4197. * @private
  4198. */
  4199. _destroyColgroupEl : function() {
  4200. var elColgroup = this._elColgroup;
  4201. if(elColgroup) {
  4202. var elTable = elColgroup.parentNode;
  4203. Ev.purgeElement(elColgroup, true);
  4204. elTable.removeChild(elColgroup);
  4205. this._elColgroup = null;
  4206. }
  4207. },
  4208. /**
  4209. * Initializes COLGROUP and COL elements for managing minWidth.
  4210. *
  4211. * @method _initColgroupEl
  4212. * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
  4213. * @private
  4214. */
  4215. _initColgroupEl : function(elTable) {
  4216. if(elTable) {
  4217. // Destroy previous
  4218. this._destroyColgroupEl();
  4219. // Add COLs to DOCUMENT FRAGMENT
  4220. var allCols = this._aColIds || [],
  4221. allKeys = this._oColumnSet.keys,
  4222. i = 0, len = allCols.length,
  4223. elCol, oColumn,
  4224. elFragment = document.createDocumentFragment(),
  4225. elColTemplate = document.createElement("col");
  4226. for(i=0,len=allKeys.length; i<len; i++) {
  4227. oColumn = allKeys[i];
  4228. elCol = elFragment.appendChild(elColTemplate.cloneNode(false));
  4229. }
  4230. // Create COLGROUP
  4231. var elColgroup = elTable.insertBefore(document.createElement("colgroup"), elTable.firstChild);
  4232. elColgroup.appendChild(elFragment);
  4233. this._elColgroup = elColgroup;
  4234. }
  4235. },
  4236. /**
  4237. * Adds a COL element to COLGROUP at given index.
  4238. *
  4239. * @method _insertColgroupColEl
  4240. * @param index {Number} Index of new COL element.
  4241. * @private
  4242. */
  4243. _insertColgroupColEl : function(index) {
  4244. if(lang.isNumber(index)&& this._elColgroup) {
  4245. var nextSibling = this._elColgroup.childNodes[index] || null;
  4246. this._elColgroup.insertBefore(document.createElement("col"), nextSibling);
  4247. }
  4248. },
  4249. /**
  4250. * Removes a COL element to COLGROUP at given index.
  4251. *
  4252. * @method _removeColgroupColEl
  4253. * @param index {Number} Index of removed COL element.
  4254. * @private
  4255. */
  4256. _removeColgroupColEl : function(index) {
  4257. if(lang.isNumber(index) && this._elColgroup && this._elColgroup.childNodes[index]) {
  4258. this._elColgroup.removeChild(this._elColgroup.childNodes[index]);
  4259. }
  4260. },
  4261. /**
  4262. * Reorders a COL element from old index(es) to new index.
  4263. *
  4264. * @method _reorderColgroupColEl
  4265. * @param aKeyIndexes {Number[]} Array of indexes of removed COL element.
  4266. * @param newIndex {Number} New index.
  4267. * @private
  4268. */
  4269. _reorderColgroupColEl : function(aKeyIndexes, newIndex) {
  4270. if(lang.isArray(aKeyIndexes) && lang.isNumber(newIndex) && this._elColgroup && (this._elColgroup.childNodes.length > aKeyIndexes[aKeyIndexes.length-1])) {
  4271. var i,
  4272. tmpCols = [];
  4273. // Remove COL
  4274. for(i=aKeyIndexes.length-1; i>-1; i--) {
  4275. tmpCols.push(this._elColgroup.removeChild(this._elColgroup.childNodes[aKeyIndexes[i]]));
  4276. }
  4277. // Insert COL
  4278. var nextSibling = this._elColgroup.childNodes[newIndex] || null;
  4279. for(i=tmpCols.length-1; i>-1; i--) {
  4280. this._elColgroup.insertBefore(tmpCols[i], nextSibling);
  4281. }
  4282. }
  4283. },
  4284. /**
  4285. * Destroy's the DataTable THEAD element, if available.
  4286. *
  4287. * @method _destroyTheadEl
  4288. * @private
  4289. */
  4290. _destroyTheadEl : function() {
  4291. var elThead = this._elThead;
  4292. if(elThead) {
  4293. var elTable = elThead.parentNode;
  4294. Ev.purgeElement(elThead, true);
  4295. this._destroyColumnHelpers();
  4296. elTable.removeChild(elThead);
  4297. this._elThead = null;
  4298. }
  4299. },
  4300. /**
  4301. * Initializes THEAD element.
  4302. *
  4303. * @method _initTheadEl
  4304. * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
  4305. * @param {HTMLElement} Initialized THEAD element.
  4306. * @private
  4307. */
  4308. _initTheadEl : function(elTable) {
  4309. elTable = elTable || this._elTable;
  4310. if(elTable) {
  4311. // Destroy previous
  4312. this._destroyTheadEl();
  4313. //TODO: append to DOM later for performance
  4314. var elThead = (this._elColgroup) ?
  4315. elTable.insertBefore(document.createElement("thead"), this._elColgroup.nextSibling) :
  4316. elTable.appendChild(document.createElement("thead"));
  4317. // Set up DOM events for THEAD
  4318. Ev.addListener(elThead, "focus", this._onTheadFocus, this);
  4319. Ev.addListener(elThead, "keydown", this._onTheadKeydown, this);
  4320. Ev.addListener(elThead, "mouseover", this._onTableMouseover, this);
  4321. Ev.addListener(elThead, "mouseout", this._onTableMouseout, this);
  4322. Ev.addListener(elThead, "mousedown", this._onTableMousedown, this);
  4323. Ev.addListener(elThead, "mouseup", this._onTableMouseup, this);
  4324. Ev.addListener(elThead, "click", this._onTheadClick, this);
  4325. // Since we can't listen for click and dblclick on the same element...
  4326. // Attach separately to THEAD and TBODY
  4327. ///Ev.addListener(elThead, "dblclick", this._onTableDblclick, this);
  4328. var oColumnSet = this._oColumnSet,
  4329. oColumn, i,j, l;
  4330. // Add TRs to the THEAD
  4331. var colTree = oColumnSet.tree;
  4332. var elTh;
  4333. for(i=0; i<colTree.length; i++) {
  4334. var elTheadTr = elThead.appendChild(document.createElement("tr"));
  4335. // ...and create TH cells
  4336. for(j=0; j<colTree[i].length; j++) {
  4337. oColumn = colTree[i][j];
  4338. elTh = elTheadTr.appendChild(document.createElement("th"));
  4339. this._initThEl(elTh,oColumn);
  4340. }
  4341. // Set FIRST/LAST on THEAD rows
  4342. if(i === 0) {
  4343. Dom.addClass(elTheadTr, DT.CLASS_FIRST);
  4344. }
  4345. if(i === (colTree.length-1)) {
  4346. Dom.addClass(elTheadTr, DT.CLASS_LAST);
  4347. }
  4348. }
  4349. // Set FIRST/LAST on edge TH elements using the values in ColumnSet headers array
  4350. var aFirstHeaders = oColumnSet.headers[0] || [];
  4351. for(i=0; i<aFirstHeaders.length; i++) {
  4352. Dom.addClass(Dom.get(this.getId() +"-th-"+aFirstHeaders[i]), DT.CLASS_FIRST);
  4353. }
  4354. var aLastHeaders = oColumnSet.headers[oColumnSet.headers.length-1] || [];
  4355. for(i=0; i<aLastHeaders.length; i++) {
  4356. Dom.addClass(Dom.get(this.getId() +"-th-"+aLastHeaders[i]), DT.CLASS_LAST);
  4357. }
  4358. YAHOO.log("TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString());
  4359. ///TODO: try _repaintGecko(this._elContainer) instead
  4360. // Bug 1806891
  4361. if(ua.webkit && ua.webkit < 420) {
  4362. var oSelf = this;
  4363. setTimeout(function() {
  4364. elThead.style.display = "";
  4365. },0);
  4366. elThead.style.display = 'none';
  4367. }
  4368. this._elThead = elThead;
  4369. // Column helpers needs _elThead to exist
  4370. this._initColumnHelpers();
  4371. }
  4372. },
  4373. /**
  4374. * Populates TH element as defined by Column.
  4375. *
  4376. * @method _initThEl
  4377. * @param elTh {HTMLElement} TH element reference.
  4378. * @param oColumn {YAHOO.widget.Column} Column object.
  4379. * @private
  4380. */
  4381. _initThEl : function(elTh, oColumn) {
  4382. elTh.id = this.getId() + "-th-" + oColumn.getSanitizedKey(); // Needed for accessibility, getColumn by TH, and ColumnDD
  4383. elTh.innerHTML = "";
  4384. elTh.rowSpan = oColumn.getRowspan();
  4385. elTh.colSpan = oColumn.getColspan();
  4386. oColumn._elTh = elTh;
  4387. var elThLiner = elTh.appendChild(document.createElement("div"));
  4388. elThLiner.id = elTh.id + "-liner"; // Needed for resizer
  4389. elThLiner.className = DT.CLASS_LINER;
  4390. oColumn._elThLiner = elThLiner;
  4391. var elThLabel = elThLiner.appendChild(document.createElement("span"));
  4392. elThLabel.className = DT.CLASS_LABEL;
  4393. // Assign abbr attribute
  4394. if(oColumn.abbr) {
  4395. elTh.abbr = oColumn.abbr;
  4396. }
  4397. // Clear minWidth on hidden Columns
  4398. if(oColumn.hidden) {
  4399. this._clearMinWidth(oColumn);
  4400. }
  4401. elTh.className = this._getColumnClassNames(oColumn);
  4402. // Set Column width...
  4403. if(oColumn.width) {
  4404. // Validate minWidth
  4405. var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
  4406. oColumn.minWidth : oColumn.width;
  4407. // ...for fallback cases
  4408. if(DT._bDynStylesFallback) {
  4409. elTh.firstChild.style.overflow = 'hidden';
  4410. elTh.firstChild.style.width = nWidth + 'px';
  4411. }
  4412. // ...for non fallback cases
  4413. else {
  4414. this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden');
  4415. }
  4416. }
  4417. this.formatTheadCell(elThLabel, oColumn, this.get("sortedBy"));
  4418. oColumn._elThLabel = elThLabel;
  4419. },
  4420. /**
  4421. * Outputs markup into the given TH based on given Column.
  4422. *
  4423. * @method DataTable.formatTheadCell
  4424. * @param elCellLabel {HTMLElement} The label SPAN element within the TH liner,
  4425. * not the liner DIV element.
  4426. * @param oColumn {YAHOO.widget.Column} Column instance.
  4427. * @param oSortedBy {Object} Sort state object literal.
  4428. */
  4429. formatTheadCell : function(elCellLabel, oColumn, oSortedBy) {
  4430. var sKey = oColumn.getKey();
  4431. var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
  4432. // Add accessibility link for sortable Columns
  4433. if(oColumn.sortable) {
  4434. // Calculate the direction
  4435. var sSortClass = this.getColumnSortDir(oColumn, oSortedBy);
  4436. var bDesc = (sSortClass === DT.CLASS_DESC);
  4437. // This is the sorted Column
  4438. if(oSortedBy && (oColumn.key === oSortedBy.key)) {
  4439. bDesc = !(oSortedBy.dir === DT.CLASS_DESC);
  4440. }
  4441. // Generate a unique HREF for visited status
  4442. var sHref = this.getId() + "-href-" + oColumn.getSanitizedKey();
  4443. // Generate a dynamic TITLE for sort status
  4444. var sTitle = (bDesc) ? this.get("MSG_SORTDESC") : this.get("MSG_SORTASC");
  4445. // Format the element
  4446. elCellLabel.innerHTML = "<a href=\"" + sHref + "\" title=\"" + sTitle + "\" class=\"" + DT.CLASS_SORTABLE + "\">" + sLabel + "</a>";
  4447. }
  4448. // Just display the label for non-sortable Columns
  4449. else {
  4450. elCellLabel.innerHTML = sLabel;
  4451. }
  4452. },
  4453. /**
  4454. * Disables DD from top-level Column TH elements.
  4455. *
  4456. * @method _destroyDraggableColumns
  4457. * @private
  4458. */
  4459. _destroyDraggableColumns : function() {
  4460. var oColumn, elTh;
  4461. for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
  4462. oColumn = this._oColumnSet.tree[0][i];
  4463. if(oColumn._dd) {
  4464. oColumn._dd = oColumn._dd.unreg();
  4465. Dom.removeClass(oColumn.getThEl(), DT.CLASS_DRAGGABLE);
  4466. }
  4467. }
  4468. },
  4469. /**
  4470. * Initializes top-level Column TH elements into DD instances.
  4471. *
  4472. * @method _initDraggableColumns
  4473. * @private
  4474. */
  4475. _initDraggableColumns : function() {
  4476. this._destroyDraggableColumns();
  4477. if(util.DD) {
  4478. var oColumn, elTh, elDragTarget;
  4479. for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
  4480. oColumn = this._oColumnSet.tree[0][i];
  4481. elTh = oColumn.getThEl();
  4482. Dom.addClass(elTh, DT.CLASS_DRAGGABLE);
  4483. elDragTarget = DT._initColumnDragTargetEl();
  4484. oColumn._dd = new YAHOO.widget.ColumnDD(this, oColumn, elTh, elDragTarget);
  4485. }
  4486. }
  4487. else {
  4488. YAHOO.log("Could not find DragDrop for draggable Columns", "warn", this.toString());
  4489. }
  4490. },
  4491. /**
  4492. * Disables resizeability on key Column TH elements.
  4493. *
  4494. * @method _destroyResizeableColumns
  4495. * @private
  4496. */
  4497. _destroyResizeableColumns : function() {
  4498. var aKeys = this._oColumnSet.keys;
  4499. for(var i=0, len=aKeys.length; i<len; i++) {
  4500. if(aKeys[i]._ddResizer) {
  4501. aKeys[i]._ddResizer = aKeys[i]._ddResizer.unreg();
  4502. Dom.removeClass(aKeys[i].getThEl(), DT.CLASS_RESIZEABLE);
  4503. }
  4504. }
  4505. },
  4506. /**
  4507. * Initializes resizeability on key Column TH elements.
  4508. *
  4509. * @method _initResizeableColumns
  4510. * @private
  4511. */
  4512. _initResizeableColumns : function() {
  4513. this._destroyResizeableColumns();
  4514. if(util.DD) {
  4515. var oColumn, elTh, elThLiner, elThResizerLiner, elThResizer, elResizerProxy, cancelClick;
  4516. for(var i=0, len=this._oColumnSet.keys.length; i<len; i++) {
  4517. oColumn = this._oColumnSet.keys[i];
  4518. if(oColumn.resizeable) {
  4519. elTh = oColumn.getThEl();
  4520. Dom.addClass(elTh, DT.CLASS_RESIZEABLE);
  4521. elThLiner = oColumn.getThLinerEl();
  4522. // Bug 1915349: So resizer is as tall as TH when rowspan > 1
  4523. // Create a separate resizer liner with position:relative
  4524. elThResizerLiner = elTh.appendChild(document.createElement("div"));
  4525. elThResizerLiner.className = DT.CLASS_RESIZERLINER;
  4526. // Move TH contents into the new resizer liner
  4527. elThResizerLiner.appendChild(elThLiner);
  4528. // Create the resizer
  4529. elThResizer = elThResizerLiner.appendChild(document.createElement("div"));
  4530. elThResizer.id = elTh.id + "-resizer"; // Needed for ColumnResizer
  4531. elThResizer.className = DT.CLASS_RESIZER;
  4532. oColumn._elResizer = elThResizer;
  4533. // Create the resizer proxy, once globally
  4534. elResizerProxy = DT._initColumnResizerProxyEl();
  4535. oColumn._ddResizer = new YAHOO.util.ColumnResizer(
  4536. this, oColumn, elTh, elThResizer, elResizerProxy);
  4537. cancelClick = function(e) {
  4538. Ev.stopPropagation(e);
  4539. };
  4540. Ev.addListener(elThResizer,"click",cancelClick);
  4541. }
  4542. }
  4543. }
  4544. else {
  4545. YAHOO.log("Could not find DragDrop for resizeable Columns", "warn", this.toString());
  4546. }
  4547. },
  4548. /**
  4549. * Destroys elements associated with Column functionality: ColumnDD and ColumnResizers.
  4550. *
  4551. * @method _destroyColumnHelpers
  4552. * @private
  4553. */
  4554. _destroyColumnHelpers : function() {
  4555. this._destroyDraggableColumns();
  4556. this._destroyResizeableColumns();
  4557. },
  4558. /**
  4559. * Initializes elements associated with Column functionality: ColumnDD and ColumnResizers.
  4560. *
  4561. * @method _initColumnHelpers
  4562. * @private
  4563. */
  4564. _initColumnHelpers : function() {
  4565. if(this.get("draggableColumns")) {
  4566. this._initDraggableColumns();
  4567. }
  4568. this._initResizeableColumns();
  4569. },
  4570. /**
  4571. * Destroy's the DataTable TBODY element, if available.
  4572. *
  4573. * @method _destroyTbodyEl
  4574. * @private
  4575. */
  4576. _destroyTbodyEl : function() {
  4577. var elTbody = this._elTbody;
  4578. if(elTbody) {
  4579. var elTable = elTbody.parentNode;
  4580. Ev.purgeElement(elTbody, true);
  4581. elTable.removeChild(elTbody);
  4582. this._elTbody = null;
  4583. }
  4584. },
  4585. /**
  4586. * Initializes TBODY element for data.
  4587. *
  4588. * @method _initTbodyEl
  4589. * @param elTable {HTMLElement} TABLE element into which to create TBODY .
  4590. * @private
  4591. */
  4592. _initTbodyEl : function(elTable) {
  4593. if(elTable) {
  4594. // Destroy previous
  4595. this._destroyTbodyEl();
  4596. // Create TBODY
  4597. var elTbody = elTable.appendChild(document.createElement("tbody"));
  4598. elTbody.tabIndex = 0;
  4599. elTbody.className = DT.CLASS_DATA;
  4600. // Set up DOM events for TBODY
  4601. Ev.addListener(elTbody, "focus", this._onTbodyFocus, this);
  4602. Ev.addListener(elTbody, "mouseover", this._onTableMouseover, this);
  4603. Ev.addListener(elTbody, "mouseout", this._onTableMouseout, this);
  4604. Ev.addListener(elTbody, "mousedown", this._onTableMousedown, this);
  4605. Ev.addListener(elTbody, "mouseup", this._onTableMouseup, this);
  4606. Ev.addListener(elTbody, "keydown", this._onTbodyKeydown, this);
  4607. Ev.addListener(elTbody, "keypress", this._onTableKeypress, this);
  4608. Ev.addListener(elTbody, "click", this._onTbodyClick, this);
  4609. // Since we can't listen for click and dblclick on the same element...
  4610. // Attach separately to THEAD and TBODY
  4611. ///Ev.addListener(elTbody, "dblclick", this._onTableDblclick, this);
  4612. // IE puts focus outline in the wrong place
  4613. if(ua.ie) {
  4614. elTbody.hideFocus=true;
  4615. }
  4616. this._elTbody = elTbody;
  4617. }
  4618. },
  4619. /**
  4620. * Destroy's the DataTable message TBODY element, if available.
  4621. *
  4622. * @method _destroyMsgTbodyEl
  4623. * @private
  4624. */
  4625. _destroyMsgTbodyEl : function() {
  4626. var elMsgTbody = this._elMsgTbody;
  4627. if(elMsgTbody) {
  4628. var elTable = elMsgTbody.parentNode;
  4629. Ev.purgeElement(elMsgTbody, true);
  4630. elTable.removeChild(elMsgTbody);
  4631. this._elTbody = null;
  4632. }
  4633. },
  4634. /**
  4635. * Initializes TBODY element for messaging.
  4636. *
  4637. * @method _initMsgTbodyEl
  4638. * @param elTable {HTMLElement} TABLE element into which to create TBODY
  4639. * @private
  4640. */
  4641. _initMsgTbodyEl : function(elTable) {
  4642. if(elTable) {
  4643. var elMsgTbody = document.createElement("tbody");
  4644. elMsgTbody.className = DT.CLASS_MESSAGE;
  4645. var elMsgTr = elMsgTbody.appendChild(document.createElement("tr"));
  4646. elMsgTr.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
  4647. this._elMsgTr = elMsgTr;
  4648. var elMsgTd = elMsgTr.appendChild(document.createElement("td"));
  4649. elMsgTd.colSpan = this._oColumnSet.keys.length || 1;
  4650. elMsgTd.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
  4651. this._elMsgTd = elMsgTd;
  4652. elMsgTbody = elTable.insertBefore(elMsgTbody, this._elTbody);
  4653. var elMsgLiner = elMsgTd.appendChild(document.createElement("div"));
  4654. elMsgLiner.className = DT.CLASS_LINER;
  4655. this._elMsgTbody = elMsgTbody;
  4656. // Set up DOM events for TBODY
  4657. Ev.addListener(elMsgTbody, "focus", this._onTbodyFocus, this);
  4658. Ev.addListener(elMsgTbody, "mouseover", this._onTableMouseover, this);
  4659. Ev.addListener(elMsgTbody, "mouseout", this._onTableMouseout, this);
  4660. Ev.addListener(elMsgTbody, "mousedown", this._onTableMousedown, this);
  4661. Ev.addListener(elMsgTbody, "mouseup", this._onTableMouseup, this);
  4662. Ev.addListener(elMsgTbody, "keydown", this._onTbodyKeydown, this);
  4663. Ev.addListener(elMsgTbody, "keypress", this._onTableKeypress, this);
  4664. Ev.addListener(elMsgTbody, "click", this._onTbodyClick, this);
  4665. }
  4666. },
  4667. /**
  4668. * Initialize internal event listeners
  4669. *
  4670. * @method _initEvents
  4671. * @private
  4672. */
  4673. _initEvents : function () {
  4674. // Initialize Column sort
  4675. this._initColumnSort();
  4676. // Add the document level click listener
  4677. YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this);
  4678. // Paginator integration
  4679. this.subscribe("paginatorChange",function () {
  4680. this._handlePaginatorChange.apply(this,arguments);
  4681. });
  4682. this.subscribe("initEvent",function () {
  4683. this.renderPaginator();
  4684. });
  4685. // Initialize CellEditor integration
  4686. this._initCellEditing();
  4687. },
  4688. /**
  4689. * Initializes Column sorting.
  4690. *
  4691. * @method _initColumnSort
  4692. * @private
  4693. */
  4694. _initColumnSort : function() {
  4695. this.subscribe("theadCellClickEvent", this.onEventSortColumn);
  4696. // Backward compatibility
  4697. var oSortedBy = this.get("sortedBy");
  4698. if(oSortedBy) {
  4699. if(oSortedBy.dir == "desc") {
  4700. this._configs.sortedBy.value.dir = DT.CLASS_DESC;
  4701. }
  4702. else if(oSortedBy.dir == "asc") {
  4703. this._configs.sortedBy.value.dir = DT.CLASS_ASC;
  4704. }
  4705. }
  4706. },
  4707. /**
  4708. * Initializes CellEditor integration.
  4709. *
  4710. * @method _initCellEditing
  4711. * @private
  4712. */
  4713. _initCellEditing : function() {
  4714. this.subscribe("editorBlurEvent",function () {
  4715. this.onEditorBlurEvent.apply(this,arguments);
  4716. });
  4717. this.subscribe("editorBlockEvent",function () {
  4718. this.onEditorBlockEvent.apply(this,arguments);
  4719. });
  4720. this.subscribe("editorUnblockEvent",function () {
  4721. this.onEditorUnblockEvent.apply(this,arguments);
  4722. });
  4723. },
  4724. // DOM MUTATION FUNCTIONS
  4725. /**
  4726. * Retruns classnames to represent current Column states.
  4727. * @method _getColumnClassnames
  4728. * @param oColumn {YAHOO.widget.Column} Column instance.
  4729. * @param aAddClasses {String[]} An array of additional classnames to add to the
  4730. * return value.
  4731. * @return {String} A String of classnames to be assigned to TH or TD elements
  4732. * for given Column.
  4733. * @private
  4734. */
  4735. _getColumnClassNames : function (oColumn, aAddClasses) {
  4736. var allClasses;
  4737. // Add CSS classes
  4738. if(lang.isString(oColumn.className)) {
  4739. // Single custom class
  4740. allClasses = [oColumn.className];
  4741. }
  4742. else if(lang.isArray(oColumn.className)) {
  4743. // Array of custom classes
  4744. allClasses = oColumn.className;
  4745. }
  4746. else {
  4747. // no custom classes
  4748. allClasses = [];
  4749. }
  4750. // Hook for setting width with via dynamic style uses key since ID is too disposable
  4751. allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey();
  4752. // Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":"
  4753. allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey();
  4754. var isSortedBy = this.get("sortedBy") || {};
  4755. // Sorted
  4756. if(oColumn.key === isSortedBy.key) {
  4757. allClasses[allClasses.length] = isSortedBy.dir || '';
  4758. }
  4759. // Hidden
  4760. if(oColumn.hidden) {
  4761. allClasses[allClasses.length] = DT.CLASS_HIDDEN;
  4762. }
  4763. // Selected
  4764. if(oColumn.selected) {
  4765. allClasses[allClasses.length] = DT.CLASS_SELECTED;
  4766. }
  4767. // Sortable
  4768. if(oColumn.sortable) {
  4769. allClasses[allClasses.length] = DT.CLASS_SORTABLE;
  4770. }
  4771. // Resizeable
  4772. if(oColumn.resizeable) {
  4773. allClasses[allClasses.length] = DT.CLASS_RESIZEABLE;
  4774. }
  4775. // Editable
  4776. if(oColumn.editor) {
  4777. allClasses[allClasses.length] = DT.CLASS_EDITABLE;
  4778. }
  4779. // Addtnl classes, including First/Last
  4780. if(aAddClasses) {
  4781. allClasses = allClasses.concat(aAddClasses);
  4782. }
  4783. return allClasses.join(' ');
  4784. },
  4785. /**
  4786. * Clears TR element template in response to any Column state change.
  4787. * @method _clearTrTemplateEl
  4788. * @private
  4789. */
  4790. _clearTrTemplateEl : function () {
  4791. this._elTrTemplate = null;
  4792. },
  4793. /**
  4794. * Returns a new TR element template with TD elements classed with current
  4795. * Column states.
  4796. * @method _getTrTemplateEl
  4797. * @return {HTMLElement} A TR element to be cloned and added to the DOM.
  4798. * @private
  4799. */
  4800. _getTrTemplateEl : function (oRecord, index) {
  4801. // Template is already available
  4802. if(this._elTrTemplate) {
  4803. return this._elTrTemplate;
  4804. }
  4805. // Template needs to be created
  4806. else {
  4807. var d = document,
  4808. tr = d.createElement('tr'),
  4809. td = d.createElement('td'),
  4810. div = d.createElement('div');
  4811. // Append the liner element
  4812. td.appendChild(div);
  4813. // Create TD elements into DOCUMENT FRAGMENT
  4814. var df = document.createDocumentFragment(),
  4815. allKeys = this._oColumnSet.keys,
  4816. elTd;
  4817. // Set state for each TD;
  4818. var aAddClasses;
  4819. for(var i=0, keysLen=allKeys.length; i<keysLen; i++) {
  4820. // Clone the TD template
  4821. elTd = td.cloneNode(true);
  4822. // Format the base TD
  4823. elTd = this._formatTdEl(allKeys[i], elTd, i, (i===keysLen-1));
  4824. df.appendChild(elTd);
  4825. }
  4826. tr.appendChild(df);
  4827. this._elTrTemplate = tr;
  4828. return tr;
  4829. }
  4830. },
  4831. /**
  4832. * Formats a basic TD element.
  4833. * @method _formatTdEl
  4834. * @param oColumn {YAHOO.widget.Column} Associated Column instance.
  4835. * @param elTd {HTMLElement} An unformatted TD element.
  4836. * @param index {Number} Column key index.
  4837. * @param isLast {Boolean} True if Column is last key of the ColumnSet.
  4838. * @return {HTMLElement} A formatted TD element.
  4839. * @private
  4840. */
  4841. _formatTdEl : function (oColumn, elTd, index, isLast) {
  4842. var oColumnSet = this._oColumnSet;
  4843. // Set the TD's accessibility headers
  4844. var allHeaders = oColumnSet.headers,
  4845. allColHeaders = allHeaders[index],
  4846. sTdHeaders = "",
  4847. sHeader;
  4848. for(var j=0, headersLen=allColHeaders.length; j < headersLen; j++) {
  4849. sHeader = this._sId + "-th-" + allColHeaders[j] + ' ';
  4850. sTdHeaders += sHeader;
  4851. }
  4852. elTd.headers = sTdHeaders;
  4853. // Class the TD element
  4854. var aAddClasses = [];
  4855. if(index === 0) {
  4856. aAddClasses[aAddClasses.length] = DT.CLASS_FIRST;
  4857. }
  4858. if(isLast) {
  4859. aAddClasses[aAddClasses.length] = DT.CLASS_LAST;
  4860. }
  4861. elTd.className = this._getColumnClassNames(oColumn, aAddClasses);
  4862. // Class the liner element
  4863. elTd.firstChild.className = DT.CLASS_LINER;
  4864. // Set Column width for fallback cases
  4865. if(oColumn.width && DT._bDynStylesFallback) {
  4866. // Validate minWidth
  4867. var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
  4868. oColumn.minWidth : oColumn.width;
  4869. elTd.firstChild.style.overflow = 'hidden';
  4870. elTd.firstChild.style.width = nWidth + 'px';
  4871. }
  4872. return elTd;
  4873. },
  4874. /**
  4875. * Create a new TR element for a given Record and appends it with the correct
  4876. * number of Column-state-classed TD elements. Striping is the responsibility of
  4877. * the calling function, which may decide to stripe the single row, a subset of
  4878. * rows, or all the rows.
  4879. * @method _createTrEl
  4880. * @param oRecord {YAHOO.widget.Record} Record instance
  4881. * @return {HTMLElement} The new TR element. This must be added to the DOM.
  4882. * @private
  4883. */
  4884. _addTrEl : function (oRecord) {
  4885. var elTrTemplate = this._getTrTemplateEl();
  4886. // Clone the TR template.
  4887. var elTr = elTrTemplate.cloneNode(true);
  4888. // Populate content
  4889. return this._updateTrEl(elTr,oRecord);
  4890. },
  4891. /**
  4892. * Formats the contents of the given TR's TD elements with data from the given
  4893. * Record. Only innerHTML should change, nothing structural.
  4894. *
  4895. * @method _updateTrEl
  4896. * @param elTr {HTMLElement} The TR element to update.
  4897. * @param oRecord {YAHOO.widget.Record} The associated Record instance.
  4898. * @return {HTMLElement} DOM reference to the new TR element.
  4899. * @private
  4900. */
  4901. _updateTrEl : function(elTr, oRecord) {
  4902. var ok = this.get("formatRow") ? this.get("formatRow").call(this, elTr, oRecord) : true;
  4903. if(ok) {
  4904. // Hide the row to prevent constant reflows
  4905. elTr.style.display = 'none';
  4906. // Update TD elements with new data
  4907. var allTds = elTr.childNodes,
  4908. elTd;
  4909. for(var i=0,len=allTds.length; i<len; ++i) {
  4910. elTd = allTds[i];
  4911. // Set the cell content
  4912. this.formatCell(allTds[i].firstChild, oRecord, this._oColumnSet.keys[i]);
  4913. }
  4914. // Redisplay the row for reflow
  4915. elTr.style.display = '';
  4916. }
  4917. elTr.id = oRecord.getId(); // Needed for Record association and tracking of FIRST/LAST
  4918. return elTr;
  4919. },
  4920. /**
  4921. * Deletes TR element by DOM reference or by DataTable page row index.
  4922. *
  4923. * @method _deleteTrEl
  4924. * @param row {HTMLElement | Number} TR element reference or Datatable page row index.
  4925. * @return {Boolean} Returns true if successful, else returns false.
  4926. * @private
  4927. */
  4928. _deleteTrEl : function(row) {
  4929. var rowIndex;
  4930. // Get page row index for the element
  4931. if(!lang.isNumber(row)) {
  4932. rowIndex = Dom.get(row).sectionRowIndex;
  4933. }
  4934. else {
  4935. rowIndex = row;
  4936. }
  4937. if(lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) {
  4938. // Cannot use tbody.deleteRow due to IE6 instability
  4939. //return this._elTbody.deleteRow(rowIndex);
  4940. return this._elTbody.removeChild(this.getTrEl(row));
  4941. }
  4942. else {
  4943. return null;
  4944. }
  4945. },
  4946. // CSS/STATE FUNCTIONS
  4947. /**
  4948. * Removes the class YAHOO.widget.DataTable.CLASS_FIRST from the first TR element
  4949. * of the DataTable page and updates internal tracker.
  4950. *
  4951. * @method _unsetFirstRow
  4952. * @private
  4953. */
  4954. _unsetFirstRow : function() {
  4955. // Remove FIRST
  4956. if(this._sFirstTrId) {
  4957. Dom.removeClass(this._sFirstTrId, DT.CLASS_FIRST);
  4958. this._sFirstTrId = null;
  4959. }
  4960. },
  4961. /**
  4962. * Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element
  4963. * of the DataTable page and updates internal tracker.
  4964. *
  4965. * @method _setFirstRow
  4966. * @private
  4967. */
  4968. _setFirstRow : function() {
  4969. this._unsetFirstRow();
  4970. var elTr = this.getFirstTrEl();
  4971. if(elTr) {
  4972. // Set FIRST
  4973. Dom.addClass(elTr, DT.CLASS_FIRST);
  4974. this._sFirstTrId = elTr.id;
  4975. }
  4976. },
  4977. /**
  4978. * Removes the class YAHOO.widget.DataTable.CLASS_LAST from the last TR element
  4979. * of the DataTable page and updates internal tracker.
  4980. *
  4981. * @method _unsetLastRow
  4982. * @private
  4983. */
  4984. _unsetLastRow : function() {
  4985. // Unassign previous class
  4986. if(this._sLastTrId) {
  4987. Dom.removeClass(this._sLastTrId, DT.CLASS_LAST);
  4988. this._sLastTrId = null;
  4989. }
  4990. },
  4991. /**
  4992. * Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element
  4993. * of the DataTable page and updates internal tracker.
  4994. *
  4995. * @method _setLastRow
  4996. * @private
  4997. */
  4998. _setLastRow : function() {
  4999. this._unsetLastRow();
  5000. var elTr = this.getLastTrEl();
  5001. if(elTr) {
  5002. // Assign class
  5003. Dom.addClass(elTr, DT.CLASS_LAST);
  5004. this._sLastTrId = elTr.id;
  5005. }
  5006. },
  5007. /**
  5008. * Assigns the classes DT.CLASS_EVEN and DT.CLASS_ODD to one, many, or all TR elements.
  5009. *
  5010. * @method _setRowStripes
  5011. * @param row {HTMLElement | String | Number} (optional) HTML TR element reference
  5012. * or string ID, or page row index of where to start striping.
  5013. * @param range {Number} (optional) If given, how many rows to stripe, otherwise
  5014. * stripe all the rows until the end.
  5015. * @private
  5016. */
  5017. _setRowStripes : function(row, range) {
  5018. // Default values stripe all rows
  5019. var allRows = this._elTbody.rows,
  5020. nStartIndex = 0,
  5021. nEndIndex = allRows.length,
  5022. aOdds = [], nOddIdx = 0,
  5023. aEvens = [], nEvenIdx = 0;
  5024. // Stripe a subset
  5025. if((row !== null) && (row !== undefined)) {
  5026. // Validate given start row
  5027. var elStartRow = this.getTrEl(row);
  5028. if(elStartRow) {
  5029. nStartIndex = elStartRow.sectionRowIndex;
  5030. // Validate given range
  5031. if(lang.isNumber(range) && (range > 1)) {
  5032. nEndIndex = nStartIndex + range;
  5033. }
  5034. }
  5035. }
  5036. for(var i=nStartIndex; i<nEndIndex; i++) {
  5037. if(i%2) {
  5038. aOdds[nOddIdx++] = allRows[i];
  5039. } else {
  5040. aEvens[nEvenIdx++] = allRows[i];
  5041. }
  5042. }
  5043. if (aOdds.length) {
  5044. Dom.replaceClass(aOdds, DT.CLASS_EVEN, DT.CLASS_ODD);
  5045. }
  5046. if (aEvens.length) {
  5047. Dom.replaceClass(aEvens, DT.CLASS_ODD, DT.CLASS_EVEN);
  5048. }
  5049. },
  5050. /**
  5051. * Assigns the class DT.CLASS_SELECTED to TR and TD elements.
  5052. *
  5053. * @method _setSelections
  5054. * @private
  5055. */
  5056. _setSelections : function() {
  5057. // Keep track of selected rows
  5058. var allSelectedRows = this.getSelectedRows();
  5059. // Keep track of selected cells
  5060. var allSelectedCells = this.getSelectedCells();
  5061. // Anything to select?
  5062. if((allSelectedRows.length>0) || (allSelectedCells.length > 0)) {
  5063. var oColumnSet = this._oColumnSet,
  5064. el;
  5065. // Loop over each row
  5066. for(var i=0; i<allSelectedRows.length; i++) {
  5067. el = Dom.get(allSelectedRows[i]);
  5068. if(el) {
  5069. Dom.addClass(el, DT.CLASS_SELECTED);
  5070. }
  5071. }
  5072. // Loop over each cell
  5073. for(i=0; i<allSelectedCells.length; i++) {
  5074. el = Dom.get(allSelectedCells[i].recordId);
  5075. if(el) {
  5076. Dom.addClass(el.childNodes[oColumnSet.getColumn(allSelectedCells[i].columnKey).getKeyIndex()], DT.CLASS_SELECTED);
  5077. }
  5078. }
  5079. }
  5080. },
  5081. /////////////////////////////////////////////////////////////////////////////
  5082. //
  5083. // Private DOM Event Handlers
  5084. //
  5085. /////////////////////////////////////////////////////////////////////////////
  5086. /**
  5087. * Validates minWidths whenever the render chain ends.
  5088. *
  5089. * @method _onRenderChainEnd
  5090. * @private
  5091. */
  5092. _onRenderChainEnd : function() {
  5093. // Hide loading message
  5094. this.hideTableMessage();
  5095. // Show empty message
  5096. if(this._elTbody.rows.length === 0) {
  5097. this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
  5098. }
  5099. // Execute in timeout thread to give implementers a chance
  5100. // to subscribe after the constructor
  5101. var oSelf = this;
  5102. setTimeout(function() {
  5103. if((oSelf instanceof DT) && oSelf._sId) {
  5104. // Init event
  5105. if(oSelf._bInit) {
  5106. oSelf._bInit = false;
  5107. oSelf.fireEvent("initEvent");
  5108. }
  5109. // Render event
  5110. oSelf.fireEvent("renderEvent");
  5111. // Backward compatibility
  5112. oSelf.fireEvent("refreshEvent");
  5113. YAHOO.log("DataTable rendered", "info", oSelf.toString());
  5114. // Post-render routine
  5115. oSelf.validateColumnWidths();
  5116. // Post-render event
  5117. oSelf.fireEvent("postRenderEvent");
  5118. /*if(YAHOO.example.Performance.trialStart) {
  5119. YAHOO.log((new Date()).getTime() - YAHOO.example.Performance.trialStart.getTime() + " ms", "time");
  5120. YAHOO.example.Performance.trialStart = null;
  5121. }*/
  5122. YAHOO.log("Post-render routine executed", "info", oSelf.toString());
  5123. }
  5124. }, 0);
  5125. },
  5126. /**
  5127. * Handles click events on the DOCUMENT.
  5128. *
  5129. * @method _onDocumentClick
  5130. * @param e {HTMLEvent} The click event.
  5131. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5132. * @private
  5133. */
  5134. _onDocumentClick : function(e, oSelf) {
  5135. var elTarget = Ev.getTarget(e);
  5136. var elTag = elTarget.nodeName.toLowerCase();
  5137. if(!Dom.isAncestor(oSelf._elContainer, elTarget)) {
  5138. oSelf.fireEvent("tableBlurEvent");
  5139. // Fires editorBlurEvent when click is not within the TABLE.
  5140. // For cases when click is within the TABLE, due to timing issues,
  5141. // the editorBlurEvent needs to get fired by the lower-level DOM click
  5142. // handlers below rather than by the TABLE click handler directly.
  5143. if(oSelf._oCellEditor) {
  5144. if(oSelf._oCellEditor.getContainerEl) {
  5145. var elContainer = oSelf._oCellEditor.getContainerEl();
  5146. // Only if the click was not within the CellEditor container
  5147. if(!Dom.isAncestor(elContainer, elTarget) &&
  5148. (elContainer.id !== elTarget.id)) {
  5149. oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
  5150. }
  5151. }
  5152. // Backward Compatibility
  5153. else if(oSelf._oCellEditor.isActive) {
  5154. // Only if the click was not within the Cell Editor container
  5155. if(!Dom.isAncestor(oSelf._oCellEditor.container, elTarget) &&
  5156. (oSelf._oCellEditor.container.id !== elTarget.id)) {
  5157. oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
  5158. }
  5159. }
  5160. }
  5161. }
  5162. },
  5163. /**
  5164. * Handles focus events on the DataTable instance.
  5165. *
  5166. * @method _onTableFocus
  5167. * @param e {HTMLEvent} The focus event.
  5168. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5169. * @private
  5170. */
  5171. _onTableFocus : function(e, oSelf) {
  5172. oSelf.fireEvent("tableFocusEvent");
  5173. },
  5174. /**
  5175. * Handles focus events on the THEAD element.
  5176. *
  5177. * @method _onTheadFocus
  5178. * @param e {HTMLEvent} The focus event.
  5179. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5180. * @private
  5181. */
  5182. _onTheadFocus : function(e, oSelf) {
  5183. oSelf.fireEvent("theadFocusEvent");
  5184. oSelf.fireEvent("tableFocusEvent");
  5185. },
  5186. /**
  5187. * Handles focus events on the TBODY element.
  5188. *
  5189. * @method _onTbodyFocus
  5190. * @param e {HTMLEvent} The focus event.
  5191. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5192. * @private
  5193. */
  5194. _onTbodyFocus : function(e, oSelf) {
  5195. oSelf.fireEvent("tbodyFocusEvent");
  5196. oSelf.fireEvent("tableFocusEvent");
  5197. },
  5198. /**
  5199. * Handles mouseover events on the DataTable instance.
  5200. *
  5201. * @method _onTableMouseover
  5202. * @param e {HTMLEvent} The mouseover event.
  5203. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5204. * @private
  5205. */
  5206. _onTableMouseover : function(e, oSelf) {
  5207. var elTarget = Ev.getTarget(e);
  5208. var elTag = elTarget.nodeName.toLowerCase();
  5209. var bKeepBubbling = true;
  5210. while(elTarget && (elTag != "table")) {
  5211. switch(elTag) {
  5212. case "body":
  5213. return;
  5214. case "a":
  5215. break;
  5216. case "td":
  5217. bKeepBubbling = oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e});
  5218. break;
  5219. case "span":
  5220. if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
  5221. bKeepBubbling = oSelf.fireEvent("theadLabelMouseoverEvent",{target:elTarget,event:e});
  5222. // Backward compatibility
  5223. bKeepBubbling = oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e});
  5224. }
  5225. break;
  5226. case "th":
  5227. bKeepBubbling = oSelf.fireEvent("theadCellMouseoverEvent",{target:elTarget,event:e});
  5228. // Backward compatibility
  5229. bKeepBubbling = oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e});
  5230. break;
  5231. case "tr":
  5232. if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
  5233. bKeepBubbling = oSelf.fireEvent("theadRowMouseoverEvent",{target:elTarget,event:e});
  5234. // Backward compatibility
  5235. bKeepBubbling = oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e});
  5236. }
  5237. else {
  5238. bKeepBubbling = oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e});
  5239. }
  5240. break;
  5241. default:
  5242. break;
  5243. }
  5244. if(bKeepBubbling === false) {
  5245. return;
  5246. }
  5247. else {
  5248. elTarget = elTarget.parentNode;
  5249. if(elTarget) {
  5250. elTag = elTarget.nodeName.toLowerCase();
  5251. }
  5252. }
  5253. }
  5254. oSelf.fireEvent("tableMouseoverEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5255. },
  5256. /**
  5257. * Handles mouseout events on the DataTable instance.
  5258. *
  5259. * @method _onTableMouseout
  5260. * @param e {HTMLEvent} The mouseout event.
  5261. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5262. * @private
  5263. */
  5264. _onTableMouseout : function(e, oSelf) {
  5265. var elTarget = Ev.getTarget(e);
  5266. var elTag = elTarget.nodeName.toLowerCase();
  5267. var bKeepBubbling = true;
  5268. while(elTarget && (elTag != "table")) {
  5269. switch(elTag) {
  5270. case "body":
  5271. return;
  5272. case "a":
  5273. break;
  5274. case "td":
  5275. bKeepBubbling = oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e});
  5276. break;
  5277. case "span":
  5278. if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
  5279. bKeepBubbling = oSelf.fireEvent("theadLabelMouseoutEvent",{target:elTarget,event:e});
  5280. // Backward compatibility
  5281. bKeepBubbling = oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e});
  5282. }
  5283. break;
  5284. case "th":
  5285. bKeepBubbling = oSelf.fireEvent("theadCellMouseoutEvent",{target:elTarget,event:e});
  5286. // Backward compatibility
  5287. bKeepBubbling = oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e});
  5288. break;
  5289. case "tr":
  5290. if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
  5291. bKeepBubbling = oSelf.fireEvent("theadRowMouseoutEvent",{target:elTarget,event:e});
  5292. // Backward compatibility
  5293. bKeepBubbling = oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e});
  5294. }
  5295. else {
  5296. bKeepBubbling = oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e});
  5297. }
  5298. break;
  5299. default:
  5300. break;
  5301. }
  5302. if(bKeepBubbling === false) {
  5303. return;
  5304. }
  5305. else {
  5306. elTarget = elTarget.parentNode;
  5307. if(elTarget) {
  5308. elTag = elTarget.nodeName.toLowerCase();
  5309. }
  5310. }
  5311. }
  5312. oSelf.fireEvent("tableMouseoutEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5313. },
  5314. /**
  5315. * Handles mousedown events on the DataTable instance.
  5316. *
  5317. * @method _onTableMousedown
  5318. * @param e {HTMLEvent} The mousedown event.
  5319. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5320. * @private
  5321. */
  5322. _onTableMousedown : function(e, oSelf) {
  5323. var elTarget = Ev.getTarget(e);
  5324. var elTag = elTarget.nodeName.toLowerCase();
  5325. var bKeepBubbling = true;
  5326. while(elTarget && (elTag != "table")) {
  5327. switch(elTag) {
  5328. case "body":
  5329. return;
  5330. case "a":
  5331. break;
  5332. case "td":
  5333. bKeepBubbling = oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e});
  5334. break;
  5335. case "span":
  5336. if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
  5337. bKeepBubbling = oSelf.fireEvent("theadLabelMousedownEvent",{target:elTarget,event:e});
  5338. // Backward compatibility
  5339. bKeepBubbling = oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e});
  5340. }
  5341. break;
  5342. case "th":
  5343. bKeepBubbling = oSelf.fireEvent("theadCellMousedownEvent",{target:elTarget,event:e});
  5344. // Backward compatibility
  5345. bKeepBubbling = oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e});
  5346. break;
  5347. case "tr":
  5348. if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
  5349. bKeepBubbling = oSelf.fireEvent("theadRowMousedownEvent",{target:elTarget,event:e});
  5350. // Backward compatibility
  5351. bKeepBubbling = oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e});
  5352. }
  5353. else {
  5354. bKeepBubbling = oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e});
  5355. }
  5356. break;
  5357. default:
  5358. break;
  5359. }
  5360. if(bKeepBubbling === false) {
  5361. return;
  5362. }
  5363. else {
  5364. elTarget = elTarget.parentNode;
  5365. if(elTarget) {
  5366. elTag = elTarget.nodeName.toLowerCase();
  5367. }
  5368. }
  5369. }
  5370. oSelf.fireEvent("tableMousedownEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5371. },
  5372. /**
  5373. * Handles mouseup events on the DataTable instance.
  5374. *
  5375. * @method _onTableMouseup
  5376. * @param e {HTMLEvent} The mouseup event.
  5377. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5378. * @private
  5379. */
  5380. _onTableMouseup : function(e, oSelf) {
  5381. var elTarget = Ev.getTarget(e);
  5382. var elTag = elTarget.nodeName.toLowerCase();
  5383. var bKeepBubbling = true;
  5384. while(elTarget && (elTag != "table")) {
  5385. switch(elTag) {
  5386. case "body":
  5387. return;
  5388. case "a":
  5389. break;
  5390. case "td":
  5391. bKeepBubbling = oSelf.fireEvent("cellMouseupEvent",{target:elTarget,event:e});
  5392. break;
  5393. case "span":
  5394. if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
  5395. bKeepBubbling = oSelf.fireEvent("theadLabelMouseupEvent",{target:elTarget,event:e});
  5396. // Backward compatibility
  5397. bKeepBubbling = oSelf.fireEvent("headerLabelMouseupEvent",{target:elTarget,event:e});
  5398. }
  5399. break;
  5400. case "th":
  5401. bKeepBubbling = oSelf.fireEvent("theadCellMouseupEvent",{target:elTarget,event:e});
  5402. // Backward compatibility
  5403. bKeepBubbling = oSelf.fireEvent("headerCellMouseupEvent",{target:elTarget,event:e});
  5404. break;
  5405. case "tr":
  5406. if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
  5407. bKeepBubbling = oSelf.fireEvent("theadRowMouseupEvent",{target:elTarget,event:e});
  5408. // Backward compatibility
  5409. bKeepBubbling = oSelf.fireEvent("headerRowMouseupEvent",{target:elTarget,event:e});
  5410. }
  5411. else {
  5412. bKeepBubbling = oSelf.fireEvent("rowMouseupEvent",{target:elTarget,event:e});
  5413. }
  5414. break;
  5415. default:
  5416. break;
  5417. }
  5418. if(bKeepBubbling === false) {
  5419. return;
  5420. }
  5421. else {
  5422. elTarget = elTarget.parentNode;
  5423. if(elTarget) {
  5424. elTag = elTarget.nodeName.toLowerCase();
  5425. }
  5426. }
  5427. }
  5428. oSelf.fireEvent("tableMouseupEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5429. },
  5430. /**
  5431. * Handles dblclick events on the DataTable instance.
  5432. *
  5433. * @method _onTableDblclick
  5434. * @param e {HTMLEvent} The dblclick event.
  5435. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5436. * @private
  5437. */
  5438. _onTableDblclick : function(e, oSelf) {
  5439. var elTarget = Ev.getTarget(e);
  5440. var elTag = elTarget.nodeName.toLowerCase();
  5441. var bKeepBubbling = true;
  5442. while(elTarget && (elTag != "table")) {
  5443. switch(elTag) {
  5444. case "body":
  5445. return;
  5446. case "td":
  5447. bKeepBubbling = oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e});
  5448. break;
  5449. case "span":
  5450. if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
  5451. bKeepBubbling = oSelf.fireEvent("theadLabelDblclickEvent",{target:elTarget,event:e});
  5452. // Backward compatibility
  5453. bKeepBubbling = oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e});
  5454. }
  5455. break;
  5456. case "th":
  5457. bKeepBubbling = oSelf.fireEvent("theadCellDblclickEvent",{target:elTarget,event:e});
  5458. // Backward compatibility
  5459. bKeepBubbling = oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e});
  5460. break;
  5461. case "tr":
  5462. if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
  5463. bKeepBubbling = oSelf.fireEvent("theadRowDblclickEvent",{target:elTarget,event:e});
  5464. // Backward compatibility
  5465. bKeepBubbling = oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e});
  5466. }
  5467. else {
  5468. bKeepBubbling = oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e});
  5469. }
  5470. break;
  5471. default:
  5472. break;
  5473. }
  5474. if(bKeepBubbling === false) {
  5475. return;
  5476. }
  5477. else {
  5478. elTarget = elTarget.parentNode;
  5479. if(elTarget) {
  5480. elTag = elTarget.nodeName.toLowerCase();
  5481. }
  5482. }
  5483. }
  5484. oSelf.fireEvent("tableDblclickEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5485. },
  5486. /**
  5487. * Handles keydown events on the THEAD element.
  5488. *
  5489. * @method _onTheadKeydown
  5490. * @param e {HTMLEvent} The key event.
  5491. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5492. * @private
  5493. */
  5494. _onTheadKeydown : function(e, oSelf) {
  5495. var elTarget = Ev.getTarget(e);
  5496. var elTag = elTarget.nodeName.toLowerCase();
  5497. var bKeepBubbling = true;
  5498. while(elTarget && (elTag != "table")) {
  5499. switch(elTag) {
  5500. case "body":
  5501. return;
  5502. case "input":
  5503. case "textarea":
  5504. // TODO: implement textareaKeyEvent
  5505. break;
  5506. case "thead":
  5507. bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
  5508. break;
  5509. default:
  5510. break;
  5511. }
  5512. if(bKeepBubbling === false) {
  5513. return;
  5514. }
  5515. else {
  5516. elTarget = elTarget.parentNode;
  5517. if(elTarget) {
  5518. elTag = elTarget.nodeName.toLowerCase();
  5519. }
  5520. }
  5521. }
  5522. oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5523. },
  5524. /**
  5525. * Handles keydown events on the TBODY element. Handles selection behavior,
  5526. * provides hooks for ENTER to edit functionality.
  5527. *
  5528. * @method _onTbodyKeydown
  5529. * @param e {HTMLEvent} The key event.
  5530. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5531. * @private
  5532. */
  5533. _onTbodyKeydown : function(e, oSelf) {
  5534. var sMode = oSelf.get("selectionMode");
  5535. if(sMode == "standard") {
  5536. oSelf._handleStandardSelectionByKey(e);
  5537. }
  5538. else if(sMode == "single") {
  5539. oSelf._handleSingleSelectionByKey(e);
  5540. }
  5541. else if(sMode == "cellblock") {
  5542. oSelf._handleCellBlockSelectionByKey(e);
  5543. }
  5544. else if(sMode == "cellrange") {
  5545. oSelf._handleCellRangeSelectionByKey(e);
  5546. }
  5547. else if(sMode == "singlecell") {
  5548. oSelf._handleSingleCellSelectionByKey(e);
  5549. }
  5550. if(oSelf._oCellEditor) {
  5551. if(oSelf._oCellEditor.fireEvent) {
  5552. oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
  5553. }
  5554. else if(oSelf._oCellEditor.isActive) {
  5555. oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
  5556. }
  5557. }
  5558. var elTarget = Ev.getTarget(e);
  5559. var elTag = elTarget.nodeName.toLowerCase();
  5560. var bKeepBubbling = true;
  5561. while(elTarget && (elTag != "table")) {
  5562. switch(elTag) {
  5563. case "body":
  5564. return;
  5565. case "tbody":
  5566. bKeepBubbling = oSelf.fireEvent("tbodyKeyEvent",{target:elTarget,event:e});
  5567. break;
  5568. default:
  5569. break;
  5570. }
  5571. if(bKeepBubbling === false) {
  5572. return;
  5573. }
  5574. else {
  5575. elTarget = elTarget.parentNode;
  5576. if(elTarget) {
  5577. elTag = elTarget.nodeName.toLowerCase();
  5578. }
  5579. }
  5580. }
  5581. oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5582. },
  5583. /**
  5584. * Handles keypress events on the TABLE. Mainly to support stopEvent on Mac.
  5585. *
  5586. * @method _onTableKeypress
  5587. * @param e {HTMLEvent} The key event.
  5588. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5589. * @private
  5590. */
  5591. _onTableKeypress : function(e, oSelf) {
  5592. if(ua.opera || (navigator.userAgent.toLowerCase().indexOf("mac") !== -1) && (ua.webkit < 420)) {
  5593. var nKey = Ev.getCharCode(e);
  5594. // arrow down
  5595. if(nKey == 40) {
  5596. Ev.stopEvent(e);
  5597. }
  5598. // arrow up
  5599. else if(nKey == 38) {
  5600. Ev.stopEvent(e);
  5601. }
  5602. }
  5603. },
  5604. /**
  5605. * Handles click events on the THEAD element.
  5606. *
  5607. * @method _onTheadClick
  5608. * @param e {HTMLEvent} The click event.
  5609. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5610. * @private
  5611. */
  5612. _onTheadClick : function(e, oSelf) {
  5613. // This blurs the CellEditor
  5614. if(oSelf._oCellEditor) {
  5615. if(oSelf._oCellEditor.fireEvent) {
  5616. oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
  5617. }
  5618. // Backward compatibility
  5619. else if(oSelf._oCellEditor.isActive) {
  5620. oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
  5621. }
  5622. }
  5623. var elTarget = Ev.getTarget(e),
  5624. elTag = elTarget.nodeName.toLowerCase(),
  5625. bKeepBubbling = true;
  5626. while(elTarget && (elTag != "table")) {
  5627. switch(elTag) {
  5628. case "body":
  5629. return;
  5630. case "input":
  5631. var sType = elTarget.type.toLowerCase();
  5632. if(sType == "checkbox") {
  5633. bKeepBubbling = oSelf.fireEvent("theadCheckboxClickEvent",{target:elTarget,event:e});
  5634. }
  5635. else if(sType == "radio") {
  5636. bKeepBubbling = oSelf.fireEvent("theadRadioClickEvent",{target:elTarget,event:e});
  5637. }
  5638. else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
  5639. bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
  5640. }
  5641. break;
  5642. case "a":
  5643. bKeepBubbling = oSelf.fireEvent("theadLinkClickEvent",{target:elTarget,event:e});
  5644. break;
  5645. case "button":
  5646. bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
  5647. break;
  5648. case "span":
  5649. if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
  5650. bKeepBubbling = oSelf.fireEvent("theadLabelClickEvent",{target:elTarget,event:e});
  5651. // Backward compatibility
  5652. bKeepBubbling = oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e});
  5653. }
  5654. break;
  5655. case "th":
  5656. bKeepBubbling = oSelf.fireEvent("theadCellClickEvent",{target:elTarget,event:e});
  5657. // Backward compatibility
  5658. bKeepBubbling = oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e});
  5659. break;
  5660. case "tr":
  5661. bKeepBubbling = oSelf.fireEvent("theadRowClickEvent",{target:elTarget,event:e});
  5662. // Backward compatibility
  5663. bKeepBubbling = oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e});
  5664. break;
  5665. default:
  5666. break;
  5667. }
  5668. if(bKeepBubbling === false) {
  5669. return;
  5670. }
  5671. else {
  5672. elTarget = elTarget.parentNode;
  5673. if(elTarget) {
  5674. elTag = elTarget.nodeName.toLowerCase();
  5675. }
  5676. }
  5677. }
  5678. oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5679. },
  5680. /**
  5681. * Handles click events on the primary TBODY element.
  5682. *
  5683. * @method _onTbodyClick
  5684. * @param e {HTMLEvent} The click event.
  5685. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5686. * @private
  5687. */
  5688. _onTbodyClick : function(e, oSelf) {
  5689. // This blurs the CellEditor
  5690. if(oSelf._oCellEditor) {
  5691. if(oSelf._oCellEditor.fireEvent) {
  5692. oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
  5693. }
  5694. else if(oSelf._oCellEditor.isActive) {
  5695. oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
  5696. }
  5697. }
  5698. // Fire Custom Events
  5699. var elTarget = Ev.getTarget(e),
  5700. elTag = elTarget.nodeName.toLowerCase(),
  5701. bKeepBubbling = true;
  5702. while(elTarget && (elTag != "table")) {
  5703. switch(elTag) {
  5704. case "body":
  5705. return;
  5706. case "input":
  5707. var sType = elTarget.type.toLowerCase();
  5708. if(sType == "checkbox") {
  5709. bKeepBubbling = oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e});
  5710. }
  5711. else if(sType == "radio") {
  5712. bKeepBubbling = oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e});
  5713. }
  5714. else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
  5715. bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
  5716. }
  5717. break;
  5718. case "a":
  5719. bKeepBubbling = oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e});
  5720. break;
  5721. case "button":
  5722. bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
  5723. break;
  5724. case "td":
  5725. bKeepBubbling = oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e});
  5726. break;
  5727. case "tr":
  5728. bKeepBubbling = oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e});
  5729. break;
  5730. default:
  5731. break;
  5732. }
  5733. if(bKeepBubbling === false) {
  5734. return;
  5735. }
  5736. else {
  5737. elTarget = elTarget.parentNode;
  5738. if(elTarget) {
  5739. elTag = elTarget.nodeName.toLowerCase();
  5740. }
  5741. }
  5742. }
  5743. oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
  5744. },
  5745. /**
  5746. * Handles change events on SELECT elements within DataTable.
  5747. *
  5748. * @method _onDropdownChange
  5749. * @param e {HTMLEvent} The change event.
  5750. * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
  5751. * @private
  5752. */
  5753. _onDropdownChange : function(e, oSelf) {
  5754. var elTarget = Ev.getTarget(e);
  5755. oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget});
  5756. },
  5757. /////////////////////////////////////////////////////////////////////////////
  5758. //
  5759. // Public member variables
  5760. //
  5761. /////////////////////////////////////////////////////////////////////////////
  5762. /**
  5763. * Returns object literal of initial configs.
  5764. *
  5765. * @property configs
  5766. * @type Object
  5767. * @default {}
  5768. */
  5769. configs: null,
  5770. /////////////////////////////////////////////////////////////////////////////
  5771. //
  5772. // Public methods
  5773. //
  5774. /////////////////////////////////////////////////////////////////////////////
  5775. /**
  5776. * Returns unique id assigned to instance, which is a useful prefix for
  5777. * generating unique DOM ID strings.
  5778. *
  5779. * @method getId
  5780. * @return {String} Unique ID of the DataSource instance.
  5781. */
  5782. getId : function() {
  5783. return this._sId;
  5784. },
  5785. /**
  5786. * DataSource instance name, for logging.
  5787. *
  5788. * @method toString
  5789. * @return {String} Unique name of the DataSource instance.
  5790. */
  5791. toString : function() {
  5792. return "DataTable instance " + this._sId;
  5793. },
  5794. /**
  5795. * Returns the DataTable instance's DataSource instance.
  5796. *
  5797. * @method getDataSource
  5798. * @return {YAHOO.util.DataSource} DataSource instance.
  5799. */
  5800. getDataSource : function() {
  5801. return this._oDataSource;
  5802. },
  5803. /**
  5804. * Returns the DataTable instance's ColumnSet instance.
  5805. *
  5806. * @method getColumnSet
  5807. * @return {YAHOO.widget.ColumnSet} ColumnSet instance.
  5808. */
  5809. getColumnSet : function() {
  5810. return this._oColumnSet;
  5811. },
  5812. /**
  5813. * Returns the DataTable instance's RecordSet instance.
  5814. *
  5815. * @method getRecordSet
  5816. * @return {YAHOO.widget.RecordSet} RecordSet instance.
  5817. */
  5818. getRecordSet : function() {
  5819. return this._oRecordSet;
  5820. },
  5821. /**
  5822. * Returns on object literal representing the DataTable instance's current
  5823. * state with the following properties:
  5824. * <dl>
  5825. * <dt>pagination</dt>
  5826. * <dd>Instance of YAHOO.widget.Paginator</dd>
  5827. *
  5828. * <dt>sortedBy</dt>
  5829. * <dd>
  5830. * <dl>
  5831. * <dt>sortedBy.key</dt>
  5832. * <dd>{String} Key of sorted Column</dd>
  5833. * <dt>sortedBy.dir</dt>
  5834. * <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
  5835. * </dl>
  5836. * </dd>
  5837. *
  5838. * <dt>selectedRows</dt>
  5839. * <dd>Array of selected rows by Record ID.</dd>
  5840. *
  5841. * <dt>selectedCells</dt>
  5842. * <dd>Selected cells as an array of object literals:
  5843. * {recordId:sRecordId, columnKey:sColumnKey}</dd>
  5844. * </dl>
  5845. *
  5846. * @method getState
  5847. * @return {Object} DataTable instance state object literal values.
  5848. */
  5849. getState : function() {
  5850. return {
  5851. totalRecords: this.get('paginator') ? this.get('paginator').get("totalRecords") : this._oRecordSet.getLength(),
  5852. pagination: this.get("paginator") ? this.get("paginator").getState() : null,
  5853. sortedBy: this.get("sortedBy"),
  5854. selectedRows: this.getSelectedRows(),
  5855. selectedCells: this.getSelectedCells()
  5856. };
  5857. },
  5858. // DOM ACCESSORS
  5859. /**
  5860. * Returns DOM reference to the DataTable's container element.
  5861. *
  5862. * @method getContainerEl
  5863. * @return {HTMLElement} Reference to DIV element.
  5864. */
  5865. getContainerEl : function() {
  5866. return this._elContainer;
  5867. },
  5868. /**
  5869. * Returns DOM reference to the DataTable's TABLE element.
  5870. *
  5871. * @method getTableEl
  5872. * @return {HTMLElement} Reference to TABLE element.
  5873. */
  5874. getTableEl : function() {
  5875. return this._elTable;
  5876. },
  5877. /**
  5878. * Returns DOM reference to the DataTable's THEAD element.
  5879. *
  5880. * @method getTheadEl
  5881. * @return {HTMLElement} Reference to THEAD element.
  5882. */
  5883. getTheadEl : function() {
  5884. return this._elThead;
  5885. },
  5886. /**
  5887. * Returns DOM reference to the DataTable's primary TBODY element.
  5888. *
  5889. * @method getTbodyEl
  5890. * @return {HTMLElement} Reference to TBODY element.
  5891. */
  5892. getTbodyEl : function() {
  5893. return this._elTbody;
  5894. },
  5895. /**
  5896. * Returns DOM reference to the DataTable's secondary TBODY element that is
  5897. * used to display messages.
  5898. *
  5899. * @method getMsgTbodyEl
  5900. * @return {HTMLElement} Reference to TBODY element.
  5901. */
  5902. getMsgTbodyEl : function() {
  5903. return this._elMsgTbody;
  5904. },
  5905. /**
  5906. * Returns DOM reference to the TD element within the secondary TBODY that is
  5907. * used to display messages.
  5908. *
  5909. * @method getMsgTdEl
  5910. * @return {HTMLElement} Reference to TD element.
  5911. */
  5912. getMsgTdEl : function() {
  5913. return this._elMsgTd;
  5914. },
  5915. /**
  5916. * Returns the corresponding TR reference for a given DOM element, ID string or
  5917. * directly page row index. If the given identifier is a child of a TR element,
  5918. * then DOM tree is traversed until a parent TR element is returned, otherwise
  5919. * null.
  5920. *
  5921. * @method getTrEl
  5922. * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to
  5923. * get: by element reference, ID string, page row index, or Record.
  5924. * @return {HTMLElement} Reference to TR element, or null.
  5925. */
  5926. getTrEl : function(row) {
  5927. // By Record
  5928. if(row instanceof YAHOO.widget.Record) {
  5929. return document.getElementById(row.getId());
  5930. }
  5931. // By page row index
  5932. else if(lang.isNumber(row)) {
  5933. var allRows = this._elTbody.rows;
  5934. return ((row > -1) && (row < allRows.length)) ? allRows[row] : null;
  5935. }
  5936. // By ID string or element reference
  5937. else {
  5938. var elRow = (lang.isString(row)) ? document.getElementById(row) : row;
  5939. // Validate HTML element
  5940. if(elRow && (elRow.ownerDocument == document)) {
  5941. // Validate TR element
  5942. if(elRow.nodeName.toLowerCase() != "tr") {
  5943. // Traverse up the DOM to find the corresponding TR element
  5944. elRow = Dom.getAncestorByTagName(elRow,"tr");
  5945. }
  5946. return elRow;
  5947. }
  5948. }
  5949. return null;
  5950. },
  5951. /**
  5952. * Returns DOM reference to the first TR element in the DataTable page, or null.
  5953. *
  5954. * @method getFirstTrEl
  5955. * @return {HTMLElement} Reference to TR element.
  5956. */
  5957. getFirstTrEl : function() {
  5958. return this._elTbody.rows[0] || null;
  5959. },
  5960. /**
  5961. * Returns DOM reference to the last TR element in the DataTable page, or null.
  5962. *
  5963. * @method getLastTrEl
  5964. * @return {HTMLElement} Reference to last TR element.
  5965. */
  5966. getLastTrEl : function() {
  5967. var allRows = this._elTbody.rows;
  5968. if(allRows.length > 0) {
  5969. return allRows[allRows.length-1] || null;
  5970. }
  5971. },
  5972. /**
  5973. * Returns DOM reference to the next TR element from the given TR element, or null.
  5974. *
  5975. * @method getNextTrEl
  5976. * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
  5977. * reference, ID string, page row index, or Record from which to get next TR element.
  5978. * @return {HTMLElement} Reference to next TR element.
  5979. */
  5980. getNextTrEl : function(row) {
  5981. var nThisTrIndex = this.getTrIndex(row);
  5982. if(nThisTrIndex !== null) {
  5983. var allRows = this._elTbody.rows;
  5984. if(nThisTrIndex < allRows.length-1) {
  5985. return allRows[nThisTrIndex+1];
  5986. }
  5987. }
  5988. YAHOO.log("Could not get next TR element for row " + row, "info", this.toString());
  5989. return null;
  5990. },
  5991. /**
  5992. * Returns DOM reference to the previous TR element from the given TR element, or null.
  5993. *
  5994. * @method getPreviousTrEl
  5995. * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
  5996. * reference, ID string, page row index, or Record from which to get previous TR element.
  5997. * @return {HTMLElement} Reference to previous TR element.
  5998. */
  5999. getPreviousTrEl : function(row) {
  6000. var nThisTrIndex = this.getTrIndex(row);
  6001. if(nThisTrIndex !== null) {
  6002. var allRows = this._elTbody.rows;
  6003. if(nThisTrIndex > 0) {
  6004. return allRows[nThisTrIndex-1];
  6005. }
  6006. }
  6007. YAHOO.log("Could not get previous TR element for row " + row, "info", this.toString());
  6008. return null;
  6009. },
  6010. /**
  6011. * Returns DOM reference to a TD liner element.
  6012. *
  6013. * @method getTdLinerEl
  6014. * @param cell {HTMLElement | Object} TD element or child of a TD element, or
  6015. * object literal of syntax {record:oRecord, column:oColumn}.
  6016. * @return {HTMLElement} Reference to TD liner element.
  6017. */
  6018. getTdLinerEl : function(cell) {
  6019. var elCell = this.getTdEl(cell);
  6020. return elCell.firstChild || null;
  6021. },
  6022. /**
  6023. * Returns DOM reference to a TD element.
  6024. *
  6025. * @method getTdEl
  6026. * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
  6027. * object literal of syntax {record:oRecord, column:oColumn}.
  6028. * @return {HTMLElement} Reference to TD element.
  6029. */
  6030. getTdEl : function(cell) {
  6031. var elCell;
  6032. var el = Dom.get(cell);
  6033. // Validate HTML element
  6034. if(el && (el.ownerDocument == document)) {
  6035. // Validate TD element
  6036. if(el.nodeName.toLowerCase() != "td") {
  6037. // Traverse up the DOM to find the corresponding TR element
  6038. elCell = Dom.getAncestorByTagName(el, "td");
  6039. }
  6040. else {
  6041. elCell = el;
  6042. }
  6043. // Make sure the TD is in this TBODY
  6044. // Bug 2527707 and bug 2263558
  6045. if(elCell && ((elCell.parentNode.parentNode == this._elTbody) || (elCell.parentNode.parentNode === null))) {
  6046. // Now we can return the TD element
  6047. return elCell;
  6048. }
  6049. }
  6050. else if(cell) {
  6051. var oRecord, nColKeyIndex;
  6052. if(lang.isString(cell.columnKey) && lang.isString(cell.recordId)) {
  6053. oRecord = this.getRecord(cell.recordId);
  6054. var oColumn = this.getColumn(cell.columnKey);
  6055. if(oColumn) {
  6056. nColKeyIndex = oColumn.getKeyIndex();
  6057. }
  6058. }
  6059. if(cell.record && cell.column && cell.column.getKeyIndex) {
  6060. oRecord = cell.record;
  6061. nColKeyIndex = cell.column.getKeyIndex();
  6062. }
  6063. var elRow = this.getTrEl(oRecord);
  6064. if((nColKeyIndex !== null) && elRow && elRow.cells && elRow.cells.length > 0) {
  6065. return elRow.cells[nColKeyIndex] || null;
  6066. }
  6067. }
  6068. return null;
  6069. },
  6070. /**
  6071. * Returns DOM reference to the first TD element in the DataTable page (by default),
  6072. * the first TD element of the optionally given row, or null.
  6073. *
  6074. * @method getFirstTdEl
  6075. * @param row {HTMLElement} (optional) row from which to get first TD
  6076. * @return {HTMLElement} Reference to TD element.
  6077. */
  6078. getFirstTdEl : function(row) {
  6079. var elRow = this.getTrEl(row) || this.getFirstTrEl();
  6080. if(elRow && (elRow.cells.length > 0)) {
  6081. return elRow.cells[0];
  6082. }
  6083. YAHOO.log("Could not get first TD element for row " + elRow, "info", this.toString());
  6084. return null;
  6085. },
  6086. /**
  6087. * Returns DOM reference to the last TD element in the DataTable page (by default),
  6088. * the first TD element of the optionally given row, or null.
  6089. *
  6090. * @method getLastTdEl
  6091. * @return {HTMLElement} Reference to last TD element.
  6092. */
  6093. getLastTdEl : function(row) {
  6094. var elRow = this.getTrEl(row) || this.getLastTrEl();
  6095. if(elRow && (elRow.cells.length > 0)) {
  6096. return elRow.cells[elRow.cells.length-1];
  6097. }
  6098. YAHOO.log("Could not get last TD element for row " + elRow, "info", this.toString());
  6099. return null;
  6100. },
  6101. /**
  6102. * Returns DOM reference to the next TD element from the given cell, or null.
  6103. *
  6104. * @method getNextTdEl
  6105. * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
  6106. * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
  6107. * @return {HTMLElement} Reference to next TD element, or null.
  6108. */
  6109. getNextTdEl : function(cell) {
  6110. var elCell = this.getTdEl(cell);
  6111. if(elCell) {
  6112. var nThisTdIndex = elCell.cellIndex;
  6113. var elRow = this.getTrEl(elCell);
  6114. if(nThisTdIndex < elRow.cells.length-1) {
  6115. return elRow.cells[nThisTdIndex+1];
  6116. }
  6117. else {
  6118. var elNextRow = this.getNextTrEl(elRow);
  6119. if(elNextRow) {
  6120. return elNextRow.cells[0];
  6121. }
  6122. }
  6123. }
  6124. YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString());
  6125. return null;
  6126. },
  6127. /**
  6128. * Returns DOM reference to the previous TD element from the given cell, or null.
  6129. *
  6130. * @method getPreviousTdEl
  6131. * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
  6132. * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
  6133. * @return {HTMLElement} Reference to previous TD element, or null.
  6134. */
  6135. getPreviousTdEl : function(cell) {
  6136. var elCell = this.getTdEl(cell);
  6137. if(elCell) {
  6138. var nThisTdIndex = elCell.cellIndex;
  6139. var elRow = this.getTrEl(elCell);
  6140. if(nThisTdIndex > 0) {
  6141. return elRow.cells[nThisTdIndex-1];
  6142. }
  6143. else {
  6144. var elPreviousRow = this.getPreviousTrEl(elRow);
  6145. if(elPreviousRow) {
  6146. return this.getLastTdEl(elPreviousRow);
  6147. }
  6148. }
  6149. }
  6150. YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString());
  6151. return null;
  6152. },
  6153. /**
  6154. * Returns DOM reference to the above TD element from the given cell, or null.
  6155. *
  6156. * @method getAboveTdEl
  6157. * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
  6158. * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
  6159. * @return {HTMLElement} Reference to next TD element, or null.
  6160. */
  6161. getAboveTdEl : function(cell) {
  6162. var elCell = this.getTdEl(cell);
  6163. if(elCell) {
  6164. var elPreviousRow = this.getPreviousTrEl(elCell);
  6165. if(elPreviousRow) {
  6166. return elPreviousRow.cells[elCell.cellIndex];
  6167. }
  6168. }
  6169. YAHOO.log("Could not get above TD element for cell " + cell, "info", this.toString());
  6170. return null;
  6171. },
  6172. /**
  6173. * Returns DOM reference to the below TD element from the given cell, or null.
  6174. *
  6175. * @method getBelowTdEl
  6176. * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
  6177. * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
  6178. * @return {HTMLElement} Reference to previous TD element, or null.
  6179. */
  6180. getBelowTdEl : function(cell) {
  6181. var elCell = this.getTdEl(cell);
  6182. if(elCell) {
  6183. var elNextRow = this.getNextTrEl(elCell);
  6184. if(elNextRow) {
  6185. return elNextRow.cells[elCell.cellIndex];
  6186. }
  6187. }
  6188. YAHOO.log("Could not get below TD element for cell " + cell, "info", this.toString());
  6189. return null;
  6190. },
  6191. /**
  6192. * Returns DOM reference to a TH liner element. Needed to normalize for resizeable
  6193. * Columns, which have an additional resizer liner DIV element between the TH
  6194. * element and the liner DIV element.
  6195. *
  6196. * @method getThLinerEl
  6197. * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
  6198. * DOM element reference, or string ID.
  6199. * @return {HTMLElement} Reference to TH liner element.
  6200. */
  6201. getThLinerEl : function(theadCell) {
  6202. var oColumn = this.getColumn(theadCell);
  6203. return (oColumn) ? oColumn.getThLinerEl() : null;
  6204. },
  6205. /**
  6206. * Returns DOM reference to a TH element.
  6207. *
  6208. * @method getThEl
  6209. * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
  6210. * DOM element reference, or string ID.
  6211. * @return {HTMLElement} Reference to TH element.
  6212. */
  6213. getThEl : function(theadCell) {
  6214. var elTh;
  6215. // Validate Column instance
  6216. if(theadCell instanceof YAHOO.widget.Column) {
  6217. var oColumn = theadCell;
  6218. elTh = oColumn.getThEl();
  6219. if(elTh) {
  6220. return elTh;
  6221. }
  6222. }
  6223. // Validate HTML element
  6224. else {
  6225. var el = Dom.get(theadCell);
  6226. if(el && (el.ownerDocument == document)) {
  6227. // Validate TH element
  6228. if(el.nodeName.toLowerCase() != "th") {
  6229. // Traverse up the DOM to find the corresponding TR element
  6230. elTh = Dom.getAncestorByTagName(el,"th");
  6231. }
  6232. else {
  6233. elTh = el;
  6234. }
  6235. return elTh;
  6236. }
  6237. }
  6238. return null;
  6239. },
  6240. /**
  6241. * Returns the page row index of given row. Returns null if the row is not on the
  6242. * current DataTable page.
  6243. *
  6244. * @method getTrIndex
  6245. * @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID
  6246. * string reference to an element within the DataTable page, a Record instance,
  6247. * or a Record's RecordSet index.
  6248. * @return {Number} Page row index, or null if row does not exist or is not on current page.
  6249. */
  6250. getTrIndex : function(row) {
  6251. var nRecordIndex;
  6252. // By Record
  6253. if(row instanceof YAHOO.widget.Record) {
  6254. nRecordIndex = this._oRecordSet.getRecordIndex(row);
  6255. if(nRecordIndex === null) {
  6256. // Not a valid Record
  6257. return null;
  6258. }
  6259. }
  6260. // Calculate page row index from Record index
  6261. else if(lang.isNumber(row)) {
  6262. nRecordIndex = row;
  6263. }
  6264. if(lang.isNumber(nRecordIndex)) {
  6265. // Validate the number
  6266. if((nRecordIndex > -1) && (nRecordIndex < this._oRecordSet.getLength())) {
  6267. // DataTable is paginated
  6268. var oPaginator = this.get('paginator');
  6269. if(oPaginator) {
  6270. // Check the record index is within the indices of the
  6271. // current page
  6272. var rng = oPaginator.getPageRecords();
  6273. if (rng && nRecordIndex >= rng[0] && nRecordIndex <= rng[1]) {
  6274. // This Record is on current page
  6275. return nRecordIndex - rng[0];
  6276. }
  6277. // This Record is not on current page
  6278. else {
  6279. return null;
  6280. }
  6281. }
  6282. // Not paginated, just return the Record index
  6283. else {
  6284. return nRecordIndex;
  6285. }
  6286. }
  6287. // RecordSet index is out of range
  6288. else {
  6289. return null;
  6290. }
  6291. }
  6292. // By element reference or ID string
  6293. else {
  6294. // Validate TR element
  6295. var elRow = this.getTrEl(row);
  6296. if(elRow && (elRow.ownerDocument == document) &&
  6297. (elRow.parentNode == this._elTbody)) {
  6298. return elRow.sectionRowIndex;
  6299. }
  6300. }
  6301. YAHOO.log("Could not get page row index for row " + row, "info", this.toString());
  6302. return null;
  6303. },
  6304. // TABLE FUNCTIONS
  6305. /**
  6306. * Resets a RecordSet with the given data and populates the page view
  6307. * with the new data. Any previous data, and selection and sort states are
  6308. * cleared. New data should be added as a separate step.
  6309. *
  6310. * @method initializeTable
  6311. */
  6312. initializeTable : function() {
  6313. // Reset init flag
  6314. this._bInit = true;
  6315. // Clear the RecordSet
  6316. this._oRecordSet.reset();
  6317. // Clear the Paginator's totalRecords if paginating
  6318. var pag = this.get('paginator');
  6319. if (pag) {
  6320. pag.set('totalRecords',0);
  6321. }
  6322. // Clear selections
  6323. this._unselectAllTrEls();
  6324. this._unselectAllTdEls();
  6325. this._aSelections = null;
  6326. this._oAnchorRecord = null;
  6327. this._oAnchorCell = null;
  6328. // Clear sort
  6329. this.set("sortedBy", null);
  6330. },
  6331. /**
  6332. * Internal wrapper calls run() on render Chain instance.
  6333. *
  6334. * @method _runRenderChain
  6335. * @private
  6336. */
  6337. _runRenderChain : function() {
  6338. this._oChainRender.run();
  6339. },
  6340. /**
  6341. * Renders the view with existing Records from the RecordSet while
  6342. * maintaining sort, pagination, and selection states. For performance, reuses
  6343. * existing DOM elements when possible while deleting extraneous elements.
  6344. *
  6345. * @method render
  6346. */
  6347. render : function() {
  6348. //YAHOO.example.Performance.trialStart = new Date();
  6349. this._oChainRender.stop();
  6350. this.fireEvent("beforeRenderEvent");
  6351. YAHOO.log("DataTable rendering...", "info", this.toString());
  6352. var i, j, k, len, allRecords;
  6353. var oPaginator = this.get('paginator');
  6354. // Paginator is enabled, show a subset of Records and update Paginator UI
  6355. if(oPaginator) {
  6356. allRecords = this._oRecordSet.getRecords(
  6357. oPaginator.getStartIndex(),
  6358. oPaginator.getRowsPerPage());
  6359. }
  6360. // Not paginated, show all records
  6361. else {
  6362. allRecords = this._oRecordSet.getRecords();
  6363. }
  6364. // From the top, update in-place existing rows, so as to reuse DOM elements
  6365. var elTbody = this._elTbody,
  6366. loopN = this.get("renderLoopSize"),
  6367. nRecordsLength = allRecords.length;
  6368. // Table has rows
  6369. if(nRecordsLength > 0) {
  6370. elTbody.style.display = "none";
  6371. while(elTbody.lastChild) {
  6372. elTbody.removeChild(elTbody.lastChild);
  6373. }
  6374. elTbody.style.display = "";
  6375. // Set up the loop Chain to render rows
  6376. this._oChainRender.add({
  6377. method: function(oArg) {
  6378. if((this instanceof DT) && this._sId) {
  6379. var i = oArg.nCurrentRecord,
  6380. endRecordIndex = ((oArg.nCurrentRecord+oArg.nLoopLength) > nRecordsLength) ?
  6381. nRecordsLength : (oArg.nCurrentRecord+oArg.nLoopLength),
  6382. elRow, nextSibling;
  6383. elTbody.style.display = "none";
  6384. for(; i<endRecordIndex; i++) {
  6385. elRow = Dom.get(allRecords[i].getId());
  6386. elRow = elRow || this._addTrEl(allRecords[i]);
  6387. nextSibling = elTbody.childNodes[i] || null;
  6388. elTbody.insertBefore(elRow, nextSibling);
  6389. }
  6390. elTbody.style.display = "";
  6391. // Set up for the next loop
  6392. oArg.nCurrentRecord = i;
  6393. }
  6394. },
  6395. scope: this,
  6396. iterations: (loopN > 0) ? Math.ceil(nRecordsLength/loopN) : 1,
  6397. argument: {
  6398. nCurrentRecord: 0,//nRecordsLength-1, // Start at first Record
  6399. nLoopLength: (loopN > 0) ? loopN : nRecordsLength
  6400. },
  6401. timeout: (loopN > 0) ? 0 : -1
  6402. });
  6403. // Post-render tasks
  6404. this._oChainRender.add({
  6405. method: function(oArg) {
  6406. if((this instanceof DT) && this._sId) {
  6407. while(elTbody.rows.length > nRecordsLength) {
  6408. elTbody.removeChild(elTbody.lastChild);
  6409. }
  6410. this._setFirstRow();
  6411. this._setLastRow();
  6412. this._setRowStripes();
  6413. this._setSelections();
  6414. }
  6415. },
  6416. scope: this,
  6417. timeout: (loopN > 0) ? 0 : -1
  6418. });
  6419. }
  6420. // Table has no rows
  6421. else {
  6422. // Set up the loop Chain to delete rows
  6423. var nTotal = elTbody.rows.length;
  6424. if(nTotal > 0) {
  6425. this._oChainRender.add({
  6426. method: function(oArg) {
  6427. if((this instanceof DT) && this._sId) {
  6428. var i = oArg.nCurrent,
  6429. loopN = oArg.nLoopLength,
  6430. nIterEnd = (i - loopN < 0) ? -1 : i - loopN;
  6431. elTbody.style.display = "none";
  6432. for(; i>nIterEnd; i--) {
  6433. elTbody.deleteRow(-1);
  6434. }
  6435. elTbody.style.display = "";
  6436. // Set up for the next loop
  6437. oArg.nCurrent = i;
  6438. }
  6439. },
  6440. scope: this,
  6441. iterations: (loopN > 0) ? Math.ceil(nTotal/loopN) : 1,
  6442. argument: {
  6443. nCurrent: nTotal,
  6444. nLoopLength: (loopN > 0) ? loopN : nTotal
  6445. },
  6446. timeout: (loopN > 0) ? 0 : -1
  6447. });
  6448. }
  6449. }
  6450. this._runRenderChain();
  6451. },
  6452. /**
  6453. * Disables DataTable UI.
  6454. *
  6455. * @method disable
  6456. */
  6457. disable : function() {
  6458. var elTable = this._elTable;
  6459. var elMask = this._elMask;
  6460. elMask.style.width = elTable.offsetWidth + "px";
  6461. elMask.style.height = elTable.offsetHeight + "px";
  6462. elMask.style.display = "";
  6463. this.fireEvent("disableEvent");
  6464. },
  6465. /**
  6466. * Undisables DataTable UI.
  6467. *
  6468. * @method undisable
  6469. */
  6470. undisable : function() {
  6471. this._elMask.style.display = "none";
  6472. this.fireEvent("undisableEvent");
  6473. },
  6474. /**
  6475. * Nulls out the entire DataTable instance and related objects, removes attached
  6476. * event listeners, and clears out DOM elements inside the container. After
  6477. * calling this method, the instance reference should be expliclitly nulled by
  6478. * implementer, as in myDataTable = null. Use with caution!
  6479. *
  6480. * @method destroy
  6481. */
  6482. destroy : function() {
  6483. // Store for later
  6484. var instanceName = this.toString();
  6485. this._oChainRender.stop();
  6486. // Destroy static resizer proxy and column proxy
  6487. DT._destroyColumnDragTargetEl();
  6488. DT._destroyColumnResizerProxyEl();
  6489. // Destroy ColumnDD and ColumnResizers
  6490. this._destroyColumnHelpers();
  6491. // Destroy all CellEditors
  6492. var oCellEditor;
  6493. for(var i=0, len=this._oColumnSet.flat.length; i<len; i++) {
  6494. oCellEditor = this._oColumnSet.flat[i].editor;
  6495. if(oCellEditor && oCellEditor.destroy) {
  6496. oCellEditor.destroy();
  6497. this._oColumnSet.flat[i].editor = null;
  6498. }
  6499. }
  6500. // Destroy Paginator
  6501. this._destroyPaginator();
  6502. // Unhook custom events
  6503. this._oRecordSet.unsubscribeAll();
  6504. this.unsubscribeAll();
  6505. // Unhook DOM events
  6506. Ev.removeListener(document, "click", this._onDocumentClick);
  6507. // Clear out the container
  6508. this._destroyContainerEl(this._elContainer);
  6509. // Null out objects
  6510. for(var param in this) {
  6511. if(lang.hasOwnProperty(this, param)) {
  6512. this[param] = null;
  6513. }
  6514. }
  6515. // Clean up static values
  6516. DT._nCurrentCount--;
  6517. if(DT._nCurrentCount < 1) {
  6518. if(DT._elDynStyleNode) {
  6519. document.getElementsByTagName('head')[0].removeChild(DT._elDynStyleNode);
  6520. DT._elDynStyleNode = null;
  6521. }
  6522. }
  6523. YAHOO.log("DataTable instance destroyed: " + instanceName);
  6524. },
  6525. /**
  6526. * Displays message within secondary TBODY.
  6527. *
  6528. * @method showTableMessage
  6529. * @param sHTML {String} (optional) Value for innerHTMlang.
  6530. * @param sClassName {String} (optional) Classname.
  6531. */
  6532. showTableMessage : function(sHTML, sClassName) {
  6533. var elCell = this._elMsgTd;
  6534. if(lang.isString(sHTML)) {
  6535. elCell.firstChild.innerHTML = sHTML;
  6536. }
  6537. if(lang.isString(sClassName)) {
  6538. elCell.className = sClassName;
  6539. }
  6540. this._elMsgTbody.style.display = "";
  6541. this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
  6542. YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString());
  6543. },
  6544. /**
  6545. * Hides secondary TBODY.
  6546. *
  6547. * @method hideTableMessage
  6548. */
  6549. hideTableMessage : function() {
  6550. if(this._elMsgTbody.style.display != "none") {
  6551. this._elMsgTbody.style.display = "none";
  6552. this._elMsgTbody.parentNode.style.width = "";
  6553. this.fireEvent("tableMsgHideEvent");
  6554. YAHOO.log("DataTable message hidden", "info", this.toString());
  6555. }
  6556. },
  6557. /**
  6558. * Brings focus to the TBODY element. Alias to focusTbodyEl.
  6559. *
  6560. * @method focus
  6561. */
  6562. focus : function() {
  6563. this.focusTbodyEl();
  6564. },
  6565. /**
  6566. * Brings focus to the THEAD element.
  6567. *
  6568. * @method focusTheadEl
  6569. */
  6570. focusTheadEl : function() {
  6571. this._focusEl(this._elThead);
  6572. },
  6573. /**
  6574. * Brings focus to the TBODY element.
  6575. *
  6576. * @method focusTbodyEl
  6577. */
  6578. focusTbodyEl : function() {
  6579. this._focusEl(this._elTbody);
  6580. },
  6581. /**
  6582. * Setting display:none on DataTable or any parent may impact width validations.
  6583. * After setting display back to "", implementers should call this method to
  6584. * manually perform those validations.
  6585. *
  6586. * @method onShow
  6587. */
  6588. onShow : function() {
  6589. this.validateColumnWidths();
  6590. for(var allKeys = this._oColumnSet.keys, i=0, len=allKeys.length, col; i<len; i++) {
  6591. col = allKeys[i];
  6592. if(col._ddResizer) {
  6593. col._ddResizer.resetResizerEl();
  6594. }
  6595. }
  6596. },
  6597. // RECORDSET FUNCTIONS
  6598. /**
  6599. * Returns Record index for given TR element or page row index.
  6600. *
  6601. * @method getRecordIndex
  6602. * @param row {YAHOO.widget.Record | HTMLElement | Number} Record instance, TR
  6603. * element reference or page row index.
  6604. * @return {Number} Record's RecordSet index, or null.
  6605. */
  6606. getRecordIndex : function(row) {
  6607. var nTrIndex;
  6608. if(!lang.isNumber(row)) {
  6609. // By Record
  6610. if(row instanceof YAHOO.widget.Record) {
  6611. return this._oRecordSet.getRecordIndex(row);
  6612. }
  6613. // By element reference
  6614. else {
  6615. // Find the TR element
  6616. var el = this.getTrEl(row);
  6617. if(el) {
  6618. nTrIndex = el.sectionRowIndex;
  6619. }
  6620. }
  6621. }
  6622. // By page row index
  6623. else {
  6624. nTrIndex = row;
  6625. }
  6626. if(lang.isNumber(nTrIndex)) {
  6627. var oPaginator = this.get("paginator");
  6628. if(oPaginator) {
  6629. return oPaginator.get('recordOffset') + nTrIndex;
  6630. }
  6631. else {
  6632. return nTrIndex;
  6633. }
  6634. }
  6635. YAHOO.log("Could not get Record index for row " + row, "info", this.toString());
  6636. return null;
  6637. },
  6638. /**
  6639. * For the given identifier, returns the associated Record instance.
  6640. *
  6641. * @method getRecord
  6642. * @param row {HTMLElement | Number | String} DOM reference to a TR element (or
  6643. * child of a TR element), RecordSet position index, or Record ID.
  6644. * @return {YAHOO.widget.Record} Record instance.
  6645. */
  6646. getRecord : function(row) {
  6647. var oRecord = this._oRecordSet.getRecord(row);
  6648. if(!oRecord) {
  6649. // Validate TR element
  6650. var elRow = this.getTrEl(row);
  6651. if(elRow) {
  6652. oRecord = this._oRecordSet.getRecord(elRow.id);
  6653. }
  6654. }
  6655. if(oRecord instanceof YAHOO.widget.Record) {
  6656. return this._oRecordSet.getRecord(oRecord);
  6657. }
  6658. else {
  6659. YAHOO.log("Could not get Record for row at " + row, "info", this.toString());
  6660. return null;
  6661. }
  6662. },
  6663. // COLUMN FUNCTIONS
  6664. /**
  6665. * For the given identifier, returns the associated Column instance. Note: For
  6666. * getting Columns by Column ID string, please use the method getColumnById().
  6667. *
  6668. * @method getColumn
  6669. * @param column {HTMLElement | String | Number} TH/TD element (or child of a
  6670. * TH/TD element), a Column key, or a ColumnSet key index.
  6671. * @return {YAHOO.widget.Column} Column instance.
  6672. */
  6673. getColumn : function(column) {
  6674. var oColumn = this._oColumnSet.getColumn(column);
  6675. if(!oColumn) {
  6676. // Validate TD element
  6677. var elCell = this.getTdEl(column);
  6678. if(elCell) {
  6679. oColumn = this._oColumnSet.getColumn(elCell.cellIndex);
  6680. }
  6681. // Validate TH element
  6682. else {
  6683. elCell = this.getThEl(column);
  6684. if(elCell) {
  6685. // Find by TH el ID
  6686. var allColumns = this._oColumnSet.flat;
  6687. for(var i=0, len=allColumns.length; i<len; i++) {
  6688. if(allColumns[i].getThEl().id === elCell.id) {
  6689. oColumn = allColumns[i];
  6690. }
  6691. }
  6692. }
  6693. }
  6694. }
  6695. if(!oColumn) {
  6696. YAHOO.log("Could not get Column for column at " + column, "info", this.toString());
  6697. }
  6698. return oColumn;
  6699. },
  6700. /**
  6701. * For the given Column ID, returns the associated Column instance. Note: For
  6702. * getting Columns by key, please use the method getColumn().
  6703. *
  6704. * @method getColumnById
  6705. * @param column {String} Column ID string.
  6706. * @return {YAHOO.widget.Column} Column instance.
  6707. */
  6708. getColumnById : function(column) {
  6709. return this._oColumnSet.getColumnById(column);
  6710. },
  6711. /**
  6712. * For the given Column instance, returns next direction to sort.
  6713. *
  6714. * @method getColumnSortDir
  6715. * @param oColumn {YAHOO.widget.Column} Column instance.
  6716. * @param oSortedBy {Object} (optional) Specify the state, or use current state.
  6717. * @return {String} YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTableCLASS_DESC.
  6718. */
  6719. getColumnSortDir : function(oColumn, oSortedBy) {
  6720. // Backward compatibility
  6721. if(oColumn.sortOptions && oColumn.sortOptions.defaultOrder) {
  6722. if(oColumn.sortOptions.defaultOrder == "asc") {
  6723. oColumn.sortOptions.defaultDir = DT.CLASS_ASC;
  6724. }
  6725. else if (oColumn.sortOptions.defaultOrder == "desc") {
  6726. oColumn.sortOptions.defaultDir = DT.CLASS_DESC;
  6727. }
  6728. }
  6729. // What is the Column's default sort direction?
  6730. var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultDir) ? oColumn.sortOptions.defaultDir : DT.CLASS_ASC;
  6731. // Is the Column currently sorted?
  6732. var bSorted = false;
  6733. oSortedBy = oSortedBy || this.get("sortedBy");
  6734. if(oSortedBy && (oSortedBy.key === oColumn.key)) {
  6735. bSorted = true;
  6736. if(oSortedBy.dir) {
  6737. sortDir = (oSortedBy.dir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
  6738. }
  6739. else {
  6740. sortDir = (sortDir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
  6741. }
  6742. }
  6743. return sortDir;
  6744. },
  6745. /**
  6746. * Overridable method gives implementers a hook to show loading message before
  6747. * sorting Column.
  6748. *
  6749. * @method doBeforeSortColumn
  6750. * @param oColumn {YAHOO.widget.Column} Column instance.
  6751. * @param sSortDir {String} YAHOO.widget.DataTable.CLASS_ASC or
  6752. * YAHOO.widget.DataTable.CLASS_DESC.
  6753. * @return {Boolean} Return true to continue sorting Column.
  6754. */
  6755. doBeforeSortColumn : function(oColumn, sSortDir) {
  6756. this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
  6757. return true;
  6758. },
  6759. /**
  6760. * Sorts given Column. If "dynamicData" is true, current selections are purged before
  6761. * a request is sent to the DataSource for data for the new state (using the
  6762. * request returned by "generateRequest()").
  6763. *
  6764. * @method sortColumn
  6765. * @param oColumn {YAHOO.widget.Column} Column instance.
  6766. * @param sDir {String} (Optional) YAHOO.widget.DataTable.CLASS_ASC or
  6767. * YAHOO.widget.DataTable.CLASS_DESC
  6768. */
  6769. sortColumn : function(oColumn, sDir) {
  6770. if(oColumn && (oColumn instanceof YAHOO.widget.Column)) {
  6771. if(!oColumn.sortable) {
  6772. Dom.addClass(this.getThEl(oColumn), DT.CLASS_SORTABLE);
  6773. }
  6774. // Validate given direction
  6775. if(sDir && (sDir !== DT.CLASS_ASC) && (sDir !== DT.CLASS_DESC)) {
  6776. sDir = null;
  6777. }
  6778. // Get the sort dir
  6779. var sSortDir = sDir || this.getColumnSortDir(oColumn);
  6780. // Is the Column currently sorted?
  6781. var oSortedBy = this.get("sortedBy") || {};
  6782. var bSorted = (oSortedBy.key === oColumn.key) ? true : false;
  6783. var ok = this.doBeforeSortColumn(oColumn, sSortDir);
  6784. if(ok) {
  6785. // Server-side sort
  6786. if(this.get("dynamicData")) {
  6787. // Get current state
  6788. var oState = this.getState();
  6789. // Reset record offset, if paginated
  6790. if(oState.pagination) {
  6791. oState.pagination.recordOffset = 0;
  6792. }
  6793. // Update sortedBy to new values
  6794. oState.sortedBy = {
  6795. key: oColumn.key,
  6796. dir: sSortDir
  6797. };
  6798. // Get the request for the new state
  6799. var request = this.get("generateRequest")(oState, this);
  6800. // Purge selections
  6801. this.unselectAllRows();
  6802. this.unselectAllCells();
  6803. // Send request for new data
  6804. var callback = {
  6805. success : this.onDataReturnSetRows,
  6806. failure : this.onDataReturnSetRows,
  6807. argument : oState, // Pass along the new state to the callback
  6808. scope : this
  6809. };
  6810. this._oDataSource.sendRequest(request, callback);
  6811. }
  6812. // Client-side sort
  6813. else {
  6814. // Is there a custom sort handler function defined?
  6815. var sortFnc = (oColumn.sortOptions && lang.isFunction(oColumn.sortOptions.sortFunction)) ?
  6816. // Custom sort function
  6817. oColumn.sortOptions.sortFunction : null;
  6818. // Sort the Records
  6819. if(!bSorted || sDir || sortFnc) {
  6820. // Shortcut for the frequently-used compare method
  6821. var compare = YAHOO.util.Sort.compare;
  6822. // Default sort function if necessary
  6823. sortFnc = sortFnc ||
  6824. function(a, b, desc, field) {
  6825. var sorted = compare(a.getData(field),b.getData(field), desc);
  6826. if(sorted === 0) {
  6827. return compare(a.getCount(),b.getCount(), desc); // Bug 1932978
  6828. }
  6829. else {
  6830. return sorted;
  6831. }
  6832. };
  6833. // Get the field to sort
  6834. var sField = (oColumn.sortOptions && oColumn.sortOptions.field) ? oColumn.sortOptions.field : oColumn.field;
  6835. // Sort the Records
  6836. this._oRecordSet.sortRecords(sortFnc, ((sSortDir == DT.CLASS_DESC) ? true : false), sField);
  6837. }
  6838. // Just reverse the Records
  6839. else {
  6840. this._oRecordSet.reverseRecords();
  6841. }
  6842. // Reset to first page if paginated
  6843. var oPaginator = this.get('paginator');
  6844. if (oPaginator) {
  6845. // Set page silently, so as not to fire change event.
  6846. oPaginator.setPage(1,true);
  6847. }
  6848. // Update UI via sortedBy
  6849. this.render();
  6850. this.set("sortedBy", {key:oColumn.key, dir:sSortDir, column:oColumn});
  6851. }
  6852. this.fireEvent("columnSortEvent",{column:oColumn,dir:sSortDir});
  6853. YAHOO.log("Column \"" + oColumn.key + "\" sorted \"" + sSortDir + "\"", "info", this.toString());
  6854. return;
  6855. }
  6856. }
  6857. YAHOO.log("Could not sort Column \"" + oColumn.key + "\"", "warn", this.toString());
  6858. },
  6859. /**
  6860. * Sets given Column to given pixel width. If new width is less than minimum
  6861. * width, sets to minimum width. Updates oColumn.width value.
  6862. *
  6863. * @method setColumnWidth
  6864. * @param oColumn {YAHOO.widget.Column} Column instance.
  6865. * @param nWidth {Number} New width in pixels. A null value auto-sizes Column,
  6866. * subject to minWidth and maxAutoWidth validations.
  6867. */
  6868. setColumnWidth : function(oColumn, nWidth) {
  6869. if(!(oColumn instanceof YAHOO.widget.Column)) {
  6870. oColumn = this.getColumn(oColumn);
  6871. }
  6872. if(oColumn) {
  6873. // Validate new width against minimum width
  6874. if(lang.isNumber(nWidth)) {
  6875. // This is why we must require a Number... :-|
  6876. nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
  6877. // Save state
  6878. oColumn.width = nWidth;
  6879. // Resize the DOM elements
  6880. this._setColumnWidth(oColumn, nWidth+"px");
  6881. this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
  6882. YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString());
  6883. }
  6884. // Unsets a width to auto-size
  6885. else if(nWidth === null) {
  6886. // Save state
  6887. oColumn.width = nWidth;
  6888. // Resize the DOM elements
  6889. this._setColumnWidth(oColumn, "auto");
  6890. this.validateColumnWidths(oColumn);
  6891. this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
  6892. YAHOO.log("Column " + oColumn + " width unset", "info", this.toString());
  6893. }
  6894. // Bug 2339454: resize then sort misaligment
  6895. this._clearTrTemplateEl();
  6896. }
  6897. else {
  6898. YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString());
  6899. }
  6900. },
  6901. /**
  6902. * Sets liner DIV elements of given Column to given width. When value should be
  6903. * auto-calculated to fit content overflow is set to visible, otherwise overflow
  6904. * is set to hidden. No validations against minimum width and no updating
  6905. * Column.width value.
  6906. *
  6907. * @method _setColumnWidth
  6908. * @param oColumn {YAHOO.widget.Column} Column instance.
  6909. * @param sWidth {String} New width value.
  6910. * @param sOverflow {String} Should be "hidden" when Column width is explicitly
  6911. * being set to a value, but should be "visible" when Column is meant to auto-fit content.
  6912. * @private
  6913. */
  6914. _setColumnWidth : function(oColumn, sWidth, sOverflow) {
  6915. if(oColumn && (oColumn.getKeyIndex() !== null)) {
  6916. sOverflow = sOverflow || (((sWidth === '') || (sWidth === 'auto')) ? 'visible' : 'hidden');
  6917. // Dynamic style algorithm
  6918. if(!DT._bDynStylesFallback) {
  6919. this._setColumnWidthDynStyles(oColumn, sWidth, sOverflow);
  6920. }
  6921. // Dynamic function algorithm
  6922. else {
  6923. this._setColumnWidthDynFunction(oColumn, sWidth, sOverflow);
  6924. }
  6925. }
  6926. else {
  6927. YAHOO.log("Could not set width of unknown Column " + oColumn + " to " + sWidth, "warn", this.toString());
  6928. }
  6929. },
  6930. /**
  6931. * Updates width of a Column's liner DIV elements by dynamically creating a
  6932. * STYLE node and writing and updating CSS style rules to it. If this fails during
  6933. * runtime, the fallback method _setColumnWidthDynFunction() will be called.
  6934. * Notes: This technique is not performant in IE6. IE7 crashes if DataTable is
  6935. * nested within another TABLE element. For these cases, it is recommended to
  6936. * use the method _setColumnWidthDynFunction by setting _bDynStylesFallback to TRUE.
  6937. *
  6938. * @method _setColumnWidthDynStyles
  6939. * @param oColumn {YAHOO.widget.Column} Column instance.
  6940. * @param sWidth {String} New width value.
  6941. * @private
  6942. */
  6943. _setColumnWidthDynStyles : function(oColumn, sWidth, sOverflow) {
  6944. var s = DT._elDynStyleNode,
  6945. rule;
  6946. // Create a new STYLE node
  6947. if(!s) {
  6948. s = document.createElement('style');
  6949. s.type = 'text/css';
  6950. s = document.getElementsByTagName('head').item(0).appendChild(s);
  6951. DT._elDynStyleNode = s;
  6952. }
  6953. // We have a STYLE node to update
  6954. if(s) {
  6955. // Use unique classname for this Column instance as a hook for resizing
  6956. var sClassname = "." + this.getId() + "-col-" + oColumn.getSanitizedKey() + " ." + DT.CLASS_LINER;
  6957. // Hide for performance
  6958. if(this._elTbody) {
  6959. this._elTbody.style.display = 'none';
  6960. }
  6961. rule = DT._oDynStyles[sClassname];
  6962. // The Column does not yet have a rule
  6963. if(!rule) {
  6964. if(s.styleSheet && s.styleSheet.addRule) {
  6965. s.styleSheet.addRule(sClassname,"overflow:"+sOverflow);
  6966. s.styleSheet.addRule(sClassname,'width:'+sWidth);
  6967. rule = s.styleSheet.rules[s.styleSheet.rules.length-1];
  6968. DT._oDynStyles[sClassname] = rule;
  6969. }
  6970. else if(s.sheet && s.sheet.insertRule) {
  6971. s.sheet.insertRule(sClassname+" {overflow:"+sOverflow+";width:"+sWidth+";}",s.sheet.cssRules.length);
  6972. rule = s.sheet.cssRules[s.sheet.cssRules.length-1];
  6973. DT._oDynStyles[sClassname] = rule;
  6974. }
  6975. }
  6976. // We have a rule to update
  6977. else {
  6978. rule.style.overflow = sOverflow;
  6979. rule.style.width = sWidth;
  6980. }
  6981. // Unhide
  6982. if(this._elTbody) {
  6983. this._elTbody.style.display = '';
  6984. }
  6985. }
  6986. // That was not a success, we must call the fallback routine
  6987. if(!rule) {
  6988. DT._bDynStylesFallback = true;
  6989. this._setColumnWidthDynFunction(oColumn, sWidth);
  6990. }
  6991. },
  6992. /**
  6993. * Updates width of a Column's liner DIV elements by dynamically creating a
  6994. * function to update all element style properties in one pass. Note: This
  6995. * technique is not supported in sandboxed environments that prohibit EVALs.
  6996. *
  6997. * @method _setColumnWidthDynFunction
  6998. * @param oColumn {YAHOO.widget.Column} Column instance.
  6999. * @param sWidth {String} New width value.
  7000. * @private
  7001. */
  7002. _setColumnWidthDynFunction : function(oColumn, sWidth, sOverflow) {
  7003. // TODO: why is this here?
  7004. if(sWidth == 'auto') {
  7005. sWidth = '';
  7006. }
  7007. // Create one function for each value of rows.length
  7008. var rowslen = this._elTbody ? this._elTbody.rows.length : 0;
  7009. // Dynamically create the function
  7010. if (!this._aDynFunctions[rowslen]) {
  7011. //Compile a custom function to do all the liner div width
  7012. //assignments at the same time. A unique function is required
  7013. //for each unique number of rows in _elTbody. This will
  7014. //result in a function declaration like:
  7015. //function (oColumn,sWidth,sOverflow) {
  7016. // var colIdx = oColumn.getKeyIndex();
  7017. // oColumn.getThLinerEl().style.overflow =
  7018. // this._elTbody.rows[0].cells[colIdx].firstChild.style.overflow =
  7019. // this._elTbody.rows[1].cells[colIdx].firstChild.style.overflow =
  7020. // ... (for all row indices in this._elTbody.rows.length - 1)
  7021. // this._elTbody.rows[99].cells[colIdx].firstChild.style.overflow =
  7022. // sOverflow;
  7023. // oColumn.getThLinerEl().style.width =
  7024. // this._elTbody.rows[0].cells[colIdx].firstChild.style.width =
  7025. // this._elTbody.rows[1].cells[colIdx].firstChild.style.width =
  7026. // ... (for all row indices in this._elTbody.rows.length - 1)
  7027. // this._elTbody.rows[99].cells[colIdx].firstChild.style.width =
  7028. // sWidth;
  7029. //}
  7030. var i,j,k;
  7031. var resizerDef = [
  7032. 'var colIdx=oColumn.getKeyIndex();',
  7033. 'oColumn.getThLinerEl().style.overflow='
  7034. ];
  7035. for (i=rowslen-1, j=2; i >= 0; --i) {
  7036. resizerDef[j++] = 'this._elTbody.rows[';
  7037. resizerDef[j++] = i;
  7038. resizerDef[j++] = '].cells[colIdx].firstChild.style.overflow=';
  7039. }
  7040. resizerDef[j] = 'sOverflow;';
  7041. resizerDef[j+1] = 'oColumn.getThLinerEl().style.width=';
  7042. for (i=rowslen-1, k=j+2; i >= 0; --i) {
  7043. resizerDef[k++] = 'this._elTbody.rows[';
  7044. resizerDef[k++] = i;
  7045. resizerDef[k++] = '].cells[colIdx].firstChild.style.width=';
  7046. }
  7047. resizerDef[k] = 'sWidth;';
  7048. this._aDynFunctions[rowslen] =
  7049. new Function('oColumn','sWidth','sOverflow',resizerDef.join(''));
  7050. }
  7051. // Get the function to execute
  7052. var resizerFn = this._aDynFunctions[rowslen];
  7053. // TODO: Hide TBODY for performance in _setColumnWidthDynFunction?
  7054. if (resizerFn) {
  7055. resizerFn.call(this,oColumn,sWidth,sOverflow);
  7056. }
  7057. },
  7058. /**
  7059. * For one or all Columns, when Column is not hidden, width is not set, and minWidth
  7060. * and/or maxAutoWidth is set, validates auto-width against minWidth and maxAutoWidth.
  7061. *
  7062. * @method validateColumnWidths
  7063. * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
  7064. */
  7065. validateColumnWidths : function(oColumn) {
  7066. var elColgroup = this._elColgroup;
  7067. var elColgroupClone = elColgroup.cloneNode(true);
  7068. var bNeedsValidation = false;
  7069. var allKeys = this._oColumnSet.keys;
  7070. var elThLiner;
  7071. // Validate just one Column's minWidth and/or maxAutoWidth
  7072. if(oColumn && !oColumn.hidden && !oColumn.width && (oColumn.getKeyIndex() !== null)) {
  7073. elThLiner = oColumn.getThLinerEl();
  7074. if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
  7075. elColgroupClone.childNodes[oColumn.getKeyIndex()].style.width =
  7076. oColumn.minWidth +
  7077. (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
  7078. (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
  7079. bNeedsValidation = true;
  7080. }
  7081. else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
  7082. this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
  7083. }
  7084. }
  7085. // Validate all Columns
  7086. else {
  7087. for(var i=0, len=allKeys.length; i<len; i++) {
  7088. oColumn = allKeys[i];
  7089. if(!oColumn.hidden && !oColumn.width) {
  7090. elThLiner = oColumn.getThLinerEl();
  7091. if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
  7092. elColgroupClone.childNodes[i].style.width =
  7093. oColumn.minWidth +
  7094. (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
  7095. (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
  7096. bNeedsValidation = true;
  7097. }
  7098. else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
  7099. this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
  7100. }
  7101. }
  7102. }
  7103. }
  7104. if(bNeedsValidation) {
  7105. elColgroup.parentNode.replaceChild(elColgroupClone, elColgroup);
  7106. this._elColgroup = elColgroupClone;
  7107. }
  7108. },
  7109. /**
  7110. * Clears minWidth.
  7111. *
  7112. * @method _clearMinWidth
  7113. * @param oColumn {YAHOO.widget.Column} Which Column.
  7114. * @private
  7115. */
  7116. _clearMinWidth : function(oColumn) {
  7117. if(oColumn.getKeyIndex() !== null) {
  7118. this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = '';
  7119. }
  7120. },
  7121. /**
  7122. * Restores minWidth.
  7123. *
  7124. * @method _restoreMinWidth
  7125. * @param oColumn {YAHOO.widget.Column} Which Column.
  7126. * @private
  7127. */
  7128. _restoreMinWidth : function(oColumn) {
  7129. if(oColumn.minWidth && (oColumn.getKeyIndex() !== null)) {
  7130. this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = oColumn.minWidth + 'px';
  7131. }
  7132. },
  7133. /**
  7134. * Hides given Column. NOTE: You cannot hide/show nested Columns. You can only
  7135. * hide/show non-nested Columns, and top-level parent Columns (which will
  7136. * hide/show all children Columns).
  7137. *
  7138. * @method hideColumn
  7139. * @param oColumn {YAHOO.widget.Column} Column instance.
  7140. */
  7141. hideColumn : function(oColumn) {
  7142. if(!(oColumn instanceof YAHOO.widget.Column)) {
  7143. oColumn = this.getColumn(oColumn);
  7144. }
  7145. // Only top-level Columns can get hidden due to issues in FF2 and SF3
  7146. if(oColumn && !oColumn.hidden && oColumn.getTreeIndex() !== null) {
  7147. var allrows = this.getTbodyEl().rows;
  7148. var l = allrows.length;
  7149. var allDescendants = this._oColumnSet.getDescendants(oColumn);
  7150. // Hide each nested Column
  7151. for(var i=0; i<allDescendants.length; i++) {
  7152. var thisColumn = allDescendants[i];
  7153. thisColumn.hidden = true;
  7154. // Style the head cell
  7155. Dom.addClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
  7156. // Does this Column have body cells?
  7157. var thisKeyIndex = thisColumn.getKeyIndex();
  7158. if(thisKeyIndex !== null) {
  7159. // Clear minWidth
  7160. this._clearMinWidth(oColumn);
  7161. // Style the body cells
  7162. for(var j=0;j<l;j++) {
  7163. Dom.addClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
  7164. }
  7165. }
  7166. this.fireEvent("columnHideEvent",{column:thisColumn});
  7167. YAHOO.log("Column \"" + oColumn.key + "\" hidden", "info", this.toString());
  7168. }
  7169. this._repaintOpera();
  7170. this._clearTrTemplateEl();
  7171. }
  7172. else {
  7173. YAHOO.log("Could not hide Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be hidden", "warn", this.toString());
  7174. }
  7175. },
  7176. /**
  7177. * Shows given Column. NOTE: You cannot hide/show nested Columns. You can only
  7178. * hide/show non-nested Columns, and top-level parent Columns (which will
  7179. * hide/show all children Columns).
  7180. *
  7181. * @method showColumn
  7182. * @param oColumn {YAHOO.widget.Column} Column instance.
  7183. */
  7184. showColumn : function(oColumn) {
  7185. if(!(oColumn instanceof YAHOO.widget.Column)) {
  7186. oColumn = this.getColumn(oColumn);
  7187. }
  7188. // Only top-level Columns can get hidden
  7189. if(oColumn && oColumn.hidden && (oColumn.getTreeIndex() !== null)) {
  7190. var allrows = this.getTbodyEl().rows;
  7191. var l = allrows.length;
  7192. var allDescendants = this._oColumnSet.getDescendants(oColumn);
  7193. // Show each nested Column
  7194. for(var i=0; i<allDescendants.length; i++) {
  7195. var thisColumn = allDescendants[i];
  7196. thisColumn.hidden = false;
  7197. // Unstyle the head cell
  7198. Dom.removeClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
  7199. // Does this Column have body cells?
  7200. var thisKeyIndex = thisColumn.getKeyIndex();
  7201. if(thisKeyIndex !== null) {
  7202. // Restore minWidth
  7203. this._restoreMinWidth(oColumn);
  7204. // Unstyle the body cells
  7205. for(var j=0;j<l;j++) {
  7206. Dom.removeClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
  7207. }
  7208. }
  7209. this.fireEvent("columnShowEvent",{column:thisColumn});
  7210. YAHOO.log("Column \"" + oColumn.key + "\" shown", "info", this.toString());
  7211. }
  7212. this._clearTrTemplateEl();
  7213. }
  7214. else {
  7215. YAHOO.log("Could not show Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be shown", "warn", this.toString());
  7216. }
  7217. },
  7218. /**
  7219. * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
  7220. * non-nested Columns, and top-level parent Columns (which will remove all
  7221. * children Columns).
  7222. *
  7223. * @method removeColumn
  7224. * @param oColumn {YAHOO.widget.Column} Column instance.
  7225. * @return oColumn {YAHOO.widget.Column} Removed Column instance.
  7226. */
  7227. removeColumn : function(oColumn) {
  7228. // Validate Column
  7229. if(!(oColumn instanceof YAHOO.widget.Column)) {
  7230. oColumn = this.getColumn(oColumn);
  7231. }
  7232. if(oColumn) {
  7233. var nColTreeIndex = oColumn.getTreeIndex();
  7234. if(nColTreeIndex !== null) {
  7235. // Which key index(es)
  7236. var i, len,
  7237. aKeyIndexes = oColumn.getKeyIndex();
  7238. // Must be a parent Column
  7239. if(aKeyIndexes === null) {
  7240. var descKeyIndexes = [];
  7241. var allDescendants = this._oColumnSet.getDescendants(oColumn);
  7242. for(i=0, len=allDescendants.length; i<len; i++) {
  7243. // Is this descendant a key Column?
  7244. var thisKey = allDescendants[i].getKeyIndex();
  7245. if(thisKey !== null) {
  7246. descKeyIndexes[descKeyIndexes.length] = thisKey;
  7247. }
  7248. }
  7249. if(descKeyIndexes.length > 0) {
  7250. aKeyIndexes = descKeyIndexes;
  7251. }
  7252. }
  7253. // Must be a key Column
  7254. else {
  7255. aKeyIndexes = [aKeyIndexes];
  7256. }
  7257. if(aKeyIndexes !== null) {
  7258. // Sort the indexes so we can remove from the right
  7259. aKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
  7260. // Destroy previous THEAD
  7261. this._destroyTheadEl();
  7262. // Create new THEAD
  7263. var aOrigColumnDefs = this._oColumnSet.getDefinitions();
  7264. oColumn = aOrigColumnDefs.splice(nColTreeIndex,1)[0];
  7265. this._initColumnSet(aOrigColumnDefs);
  7266. this._initTheadEl();
  7267. // Remove COL
  7268. for(i=aKeyIndexes.length-1; i>-1; i--) {
  7269. this._removeColgroupColEl(aKeyIndexes[i]);
  7270. }
  7271. // Remove TD
  7272. var allRows = this._elTbody.rows;
  7273. if(allRows.length > 0) {
  7274. var loopN = this.get("renderLoopSize"),
  7275. loopEnd = allRows.length;
  7276. this._oChainRender.add({
  7277. method: function(oArg) {
  7278. if((this instanceof DT) && this._sId) {
  7279. var i = oArg.nCurrentRow,
  7280. len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
  7281. aIndexes = oArg.aIndexes,
  7282. j;
  7283. for(; i < len; ++i) {
  7284. for(j = aIndexes.length-1; j>-1; j--) {
  7285. allRows[i].removeChild(allRows[i].childNodes[aIndexes[j]]);
  7286. }
  7287. }
  7288. oArg.nCurrentRow = i;
  7289. }
  7290. },
  7291. iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
  7292. argument: {nCurrentRow:0, aIndexes:aKeyIndexes},
  7293. scope: this,
  7294. timeout: (loopN > 0) ? 0 : -1
  7295. });
  7296. this._runRenderChain();
  7297. }
  7298. this.fireEvent("columnRemoveEvent",{column:oColumn});
  7299. YAHOO.log("Column \"" + oColumn.key + "\" removed", "info", this.toString());
  7300. return oColumn;
  7301. }
  7302. }
  7303. }
  7304. YAHOO.log("Could not remove Column \"" + oColumn.key + "\". Only non-nested Columns can be removed", "warn", this.toString());
  7305. },
  7306. /**
  7307. * Inserts given Column at the index if given, otherwise at the end. NOTE: You
  7308. * can only add non-nested Columns and top-level parent Columns. You cannot add
  7309. * a nested Column to an existing parent.
  7310. *
  7311. * @method insertColumn
  7312. * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
  7313. * definition or a Column instance.
  7314. * @param index {Number} (optional) New tree index.
  7315. * @return oColumn {YAHOO.widget.Column} Inserted Column instance.
  7316. */
  7317. insertColumn : function(oColumn, index) {
  7318. // Validate Column
  7319. if(oColumn instanceof YAHOO.widget.Column) {
  7320. oColumn = oColumn.getDefinition();
  7321. }
  7322. else if(oColumn.constructor !== Object) {
  7323. YAHOO.log("Could not insert Column \"" + oColumn + "\" due to invalid argument", "warn", this.toString());
  7324. return;
  7325. }
  7326. // Validate index or append new Column to the end of the ColumnSet
  7327. var oColumnSet = this._oColumnSet;
  7328. if(!lang.isValue(index) || !lang.isNumber(index)) {
  7329. index = oColumnSet.tree[0].length;
  7330. }
  7331. // Destroy previous THEAD
  7332. this._destroyTheadEl();
  7333. // Create new THEAD
  7334. var aNewColumnDefs = this._oColumnSet.getDefinitions();
  7335. aNewColumnDefs.splice(index, 0, oColumn);
  7336. this._initColumnSet(aNewColumnDefs);
  7337. this._initTheadEl();
  7338. // Need to refresh the reference
  7339. oColumnSet = this._oColumnSet;
  7340. var oNewColumn = oColumnSet.tree[0][index];
  7341. // Get key index(es) for new Column
  7342. var i, len,
  7343. descKeyIndexes = [];
  7344. var allDescendants = oColumnSet.getDescendants(oNewColumn);
  7345. for(i=0, len=allDescendants.length; i<len; i++) {
  7346. // Is this descendant a key Column?
  7347. var thisKey = allDescendants[i].getKeyIndex();
  7348. if(thisKey !== null) {
  7349. descKeyIndexes[descKeyIndexes.length] = thisKey;
  7350. }
  7351. }
  7352. if(descKeyIndexes.length > 0) {
  7353. // Sort the indexes
  7354. var newIndex = descKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
  7355. // Add COL
  7356. for(i=descKeyIndexes.length-1; i>-1; i--) {
  7357. this._insertColgroupColEl(descKeyIndexes[i]);
  7358. }
  7359. // Add TD
  7360. var allRows = this._elTbody.rows;
  7361. if(allRows.length > 0) {
  7362. var loopN = this.get("renderLoopSize"),
  7363. loopEnd = allRows.length;
  7364. // Get templates for each new TD
  7365. var aTdTemplates = [],
  7366. elTdTemplate;
  7367. for(i=0, len=descKeyIndexes.length; i<len; i++) {
  7368. var thisKeyIndex = descKeyIndexes[i];
  7369. elTdTemplate = this._getTrTemplateEl().childNodes[i].cloneNode(true);
  7370. elTdTemplate = this._formatTdEl(this._oColumnSet.keys[thisKeyIndex], elTdTemplate, thisKeyIndex, (thisKeyIndex===this._oColumnSet.keys.length-1));
  7371. aTdTemplates[thisKeyIndex] = elTdTemplate;
  7372. }
  7373. this._oChainRender.add({
  7374. method: function(oArg) {
  7375. if((this instanceof DT) && this._sId) {
  7376. var i = oArg.nCurrentRow, j,
  7377. descKeyIndexes = oArg.descKeyIndexes,
  7378. len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
  7379. nextSibling;
  7380. for(; i < len; ++i) {
  7381. nextSibling = allRows[i].childNodes[newIndex] || null;
  7382. for(j=descKeyIndexes.length-1; j>-1; j--) {
  7383. allRows[i].insertBefore(oArg.aTdTemplates[descKeyIndexes[j]].cloneNode(true), nextSibling);
  7384. }
  7385. }
  7386. oArg.nCurrentRow = i;
  7387. }
  7388. },
  7389. iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
  7390. argument: {nCurrentRow:0,aTdTemplates:aTdTemplates,descKeyIndexes:descKeyIndexes},
  7391. scope: this,
  7392. timeout: (loopN > 0) ? 0 : -1
  7393. });
  7394. this._runRenderChain();
  7395. }
  7396. this.fireEvent("columnInsertEvent",{column:oColumn,index:index});
  7397. YAHOO.log("Column \"" + oColumn.key + "\" inserted into index " + index, "info", this.toString());
  7398. return oNewColumn;
  7399. }
  7400. },
  7401. /**
  7402. * Removes given Column and inserts into given tree index. NOTE: You
  7403. * can only reorder non-nested Columns and top-level parent Columns. You cannot
  7404. * reorder a nested Column to an existing parent.
  7405. *
  7406. * @method reorderColumn
  7407. * @param oColumn {YAHOO.widget.Column} Column instance.
  7408. * @param index {Number} New tree index.
  7409. * @return oColumn {YAHOO.widget.Column} Reordered Column instance.
  7410. */
  7411. reorderColumn : function(oColumn, index) {
  7412. // Validate Column and new index
  7413. if(!(oColumn instanceof YAHOO.widget.Column)) {
  7414. oColumn = this.getColumn(oColumn);
  7415. }
  7416. if(oColumn && YAHOO.lang.isNumber(index)) {
  7417. var nOrigTreeIndex = oColumn.getTreeIndex();
  7418. if((nOrigTreeIndex !== null) && (nOrigTreeIndex !== index)) {
  7419. // Which key index(es)
  7420. var i, len,
  7421. aOrigKeyIndexes = oColumn.getKeyIndex(),
  7422. allDescendants,
  7423. descKeyIndexes = [],
  7424. thisKey;
  7425. // Must be a parent Column...
  7426. if(aOrigKeyIndexes === null) {
  7427. allDescendants = this._oColumnSet.getDescendants(oColumn);
  7428. for(i=0, len=allDescendants.length; i<len; i++) {
  7429. // Is this descendant a key Column?
  7430. thisKey = allDescendants[i].getKeyIndex();
  7431. if(thisKey !== null) {
  7432. descKeyIndexes[descKeyIndexes.length] = thisKey;
  7433. }
  7434. }
  7435. if(descKeyIndexes.length > 0) {
  7436. aOrigKeyIndexes = descKeyIndexes;
  7437. }
  7438. }
  7439. // ...or else must be a key Column
  7440. else {
  7441. aOrigKeyIndexes = [aOrigKeyIndexes];
  7442. }
  7443. if(aOrigKeyIndexes !== null) {
  7444. // Sort the indexes
  7445. aOrigKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
  7446. // Destroy previous THEAD
  7447. this._destroyTheadEl();
  7448. // Create new THEAD
  7449. var aColumnDefs = this._oColumnSet.getDefinitions();
  7450. var oColumnDef = aColumnDefs.splice(nOrigTreeIndex,1)[0];
  7451. aColumnDefs.splice(index, 0, oColumnDef);
  7452. this._initColumnSet(aColumnDefs);
  7453. this._initTheadEl();
  7454. // Need to refresh the reference
  7455. var oNewColumn = this._oColumnSet.tree[0][index];
  7456. // What are new key index(es)
  7457. var aNewKeyIndexes = oNewColumn.getKeyIndex();
  7458. // Must be a parent Column
  7459. if(aNewKeyIndexes === null) {
  7460. descKeyIndexes = [];
  7461. allDescendants = this._oColumnSet.getDescendants(oNewColumn);
  7462. for(i=0, len=allDescendants.length; i<len; i++) {
  7463. // Is this descendant a key Column?
  7464. thisKey = allDescendants[i].getKeyIndex();
  7465. if(thisKey !== null) {
  7466. descKeyIndexes[descKeyIndexes.length] = thisKey;
  7467. }
  7468. }
  7469. if(descKeyIndexes.length > 0) {
  7470. aNewKeyIndexes = descKeyIndexes;
  7471. }
  7472. }
  7473. // Must be a key Column
  7474. else {
  7475. aNewKeyIndexes = [aNewKeyIndexes];
  7476. }
  7477. // Sort the new indexes and grab the first one for the new location
  7478. var newIndex = aNewKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
  7479. // Reorder COL
  7480. this._reorderColgroupColEl(aOrigKeyIndexes, newIndex);
  7481. // Reorder TD
  7482. var allRows = this._elTbody.rows;
  7483. if(allRows.length > 0) {
  7484. var loopN = this.get("renderLoopSize"),
  7485. loopEnd = allRows.length;
  7486. this._oChainRender.add({
  7487. method: function(oArg) {
  7488. if((this instanceof DT) && this._sId) {
  7489. var i = oArg.nCurrentRow, j, tmpTds, nextSibling,
  7490. len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
  7491. aIndexes = oArg.aIndexes, thisTr;
  7492. // For each row
  7493. for(; i < len; ++i) {
  7494. tmpTds = [];
  7495. thisTr = allRows[i];
  7496. // Remove each TD
  7497. for(j=aIndexes.length-1; j>-1; j--) {
  7498. tmpTds.push(thisTr.removeChild(thisTr.childNodes[aIndexes[j]]));
  7499. }
  7500. // Insert each TD
  7501. nextSibling = thisTr.childNodes[newIndex] || null;
  7502. for(j=tmpTds.length-1; j>-1; j--) {
  7503. thisTr.insertBefore(tmpTds[j], nextSibling);
  7504. }
  7505. }
  7506. oArg.nCurrentRow = i;
  7507. }
  7508. },
  7509. iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
  7510. argument: {nCurrentRow:0, aIndexes:aOrigKeyIndexes},
  7511. scope: this,
  7512. timeout: (loopN > 0) ? 0 : -1
  7513. });
  7514. this._runRenderChain();
  7515. }
  7516. this.fireEvent("columnReorderEvent",{column:oNewColumn});
  7517. YAHOO.log("Column \"" + oNewColumn.key + "\" reordered", "info", this.toString());
  7518. return oNewColumn;
  7519. }
  7520. }
  7521. }
  7522. YAHOO.log("Could not reorder Column \"" + oColumn.key + "\". Only non-nested Columns can be reordered", "warn", this.toString());
  7523. },
  7524. /**
  7525. * Selects given Column. NOTE: You cannot select/unselect nested Columns. You can only
  7526. * select/unselect non-nested Columns, and bottom-level key Columns.
  7527. *
  7528. * @method selectColumn
  7529. * @param column {HTMLElement | String | Number} DOM reference or ID string to a
  7530. * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
  7531. */
  7532. selectColumn : function(oColumn) {
  7533. oColumn = this.getColumn(oColumn);
  7534. if(oColumn && !oColumn.selected) {
  7535. // Only bottom-level Columns can get hidden
  7536. if(oColumn.getKeyIndex() !== null) {
  7537. oColumn.selected = true;
  7538. // Update head cell
  7539. var elTh = oColumn.getThEl();
  7540. Dom.addClass(elTh,DT.CLASS_SELECTED);
  7541. // Update body cells
  7542. var allRows = this.getTbodyEl().rows;
  7543. var oChainRender = this._oChainRender;
  7544. oChainRender.add({
  7545. method: function(oArg) {
  7546. if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
  7547. Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);
  7548. }
  7549. oArg.rowIndex++;
  7550. },
  7551. scope: this,
  7552. iterations: allRows.length,
  7553. argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
  7554. });
  7555. this._clearTrTemplateEl();
  7556. this._elTbody.style.display = "none";
  7557. this._runRenderChain();
  7558. this._elTbody.style.display = "";
  7559. this.fireEvent("columnSelectEvent",{column:oColumn});
  7560. YAHOO.log("Column \"" + oColumn.key + "\" selected", "info", this.toString());
  7561. }
  7562. else {
  7563. YAHOO.log("Could not select Column \"" + oColumn.key + "\". Only non-nested Columns can be selected", "warn", this.toString());
  7564. }
  7565. }
  7566. },
  7567. /**
  7568. * Unselects given Column. NOTE: You cannot select/unselect nested Columns. You can only
  7569. * select/unselect non-nested Columns, and bottom-level key Columns.
  7570. *
  7571. * @method unselectColumn
  7572. * @param column {HTMLElement | String | Number} DOM reference or ID string to a
  7573. * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
  7574. */
  7575. unselectColumn : function(oColumn) {
  7576. oColumn = this.getColumn(oColumn);
  7577. if(oColumn && oColumn.selected) {
  7578. // Only bottom-level Columns can get hidden
  7579. if(oColumn.getKeyIndex() !== null) {
  7580. oColumn.selected = false;
  7581. // Update head cell
  7582. var elTh = oColumn.getThEl();
  7583. Dom.removeClass(elTh,DT.CLASS_SELECTED);
  7584. // Update body cells
  7585. var allRows = this.getTbodyEl().rows;
  7586. var oChainRender = this._oChainRender;
  7587. oChainRender.add({
  7588. method: function(oArg) {
  7589. if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
  7590. Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);
  7591. }
  7592. oArg.rowIndex++;
  7593. },
  7594. scope: this,
  7595. iterations:allRows.length,
  7596. argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
  7597. });
  7598. this._clearTrTemplateEl();
  7599. this._elTbody.style.display = "none";
  7600. this._runRenderChain();
  7601. this._elTbody.style.display = "";
  7602. this.fireEvent("columnUnselectEvent",{column:oColumn});
  7603. YAHOO.log("Column \"" + oColumn.key + "\" unselected", "info", this.toString());
  7604. }
  7605. else {
  7606. YAHOO.log("Could not unselect Column \"" + oColumn.key + "\". Only non-nested Columns can be unselected", "warn", this.toString());
  7607. }
  7608. }
  7609. },
  7610. /**
  7611. * Returns an array selected Column instances.
  7612. *
  7613. * @method getSelectedColumns
  7614. * @return {YAHOO.widget.Column[]} Array of Column instances.
  7615. */
  7616. getSelectedColumns : function(oColumn) {
  7617. var selectedColumns = [];
  7618. var aKeys = this._oColumnSet.keys;
  7619. for(var i=0,len=aKeys.length; i<len; i++) {
  7620. if(aKeys[i].selected) {
  7621. selectedColumns[selectedColumns.length] = aKeys[i];
  7622. }
  7623. }
  7624. return selectedColumns;
  7625. },
  7626. /**
  7627. * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
  7628. * NOTE: You cannot highlight/unhighlight nested Columns. You can only
  7629. * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
  7630. *
  7631. * @method highlightColumn
  7632. * @param column {HTMLElement | String | Number} DOM reference or ID string to a
  7633. * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
  7634. */
  7635. highlightColumn : function(column) {
  7636. var oColumn = this.getColumn(column);
  7637. // Only bottom-level Columns can get highlighted
  7638. if(oColumn && (oColumn.getKeyIndex() !== null)) {
  7639. // Update head cell
  7640. var elTh = oColumn.getThEl();
  7641. Dom.addClass(elTh,DT.CLASS_HIGHLIGHTED);
  7642. // Update body cells
  7643. var allRows = this.getTbodyEl().rows;
  7644. var oChainRender = this._oChainRender;
  7645. oChainRender.add({
  7646. method: function(oArg) {
  7647. if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
  7648. Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
  7649. }
  7650. oArg.rowIndex++;
  7651. },
  7652. scope: this,
  7653. iterations:allRows.length,
  7654. argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
  7655. timeout: -1
  7656. });
  7657. this._elTbody.style.display = "none";
  7658. this._runRenderChain();
  7659. this._elTbody.style.display = "";
  7660. this.fireEvent("columnHighlightEvent",{column:oColumn});
  7661. YAHOO.log("Column \"" + oColumn.key + "\" highlighed", "info", this.toString());
  7662. }
  7663. else {
  7664. YAHOO.log("Could not highlight Column \"" + oColumn.key + "\". Only non-nested Columns can be highlighted", "warn", this.toString());
  7665. }
  7666. },
  7667. /**
  7668. * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
  7669. * NOTE: You cannot highlight/unhighlight nested Columns. You can only
  7670. * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
  7671. *
  7672. * @method unhighlightColumn
  7673. * @param column {HTMLElement | String | Number} DOM reference or ID string to a
  7674. * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
  7675. */
  7676. unhighlightColumn : function(column) {
  7677. var oColumn = this.getColumn(column);
  7678. // Only bottom-level Columns can get highlighted
  7679. if(oColumn && (oColumn.getKeyIndex() !== null)) {
  7680. // Update head cell
  7681. var elTh = oColumn.getThEl();
  7682. Dom.removeClass(elTh,DT.CLASS_HIGHLIGHTED);
  7683. // Update body cells
  7684. var allRows = this.getTbodyEl().rows;
  7685. var oChainRender = this._oChainRender;
  7686. oChainRender.add({
  7687. method: function(oArg) {
  7688. if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
  7689. Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
  7690. }
  7691. oArg.rowIndex++;
  7692. },
  7693. scope: this,
  7694. iterations:allRows.length,
  7695. argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
  7696. timeout: -1
  7697. });
  7698. this._elTbody.style.display = "none";
  7699. this._runRenderChain();
  7700. this._elTbody.style.display = "";
  7701. this.fireEvent("columnUnhighlightEvent",{column:oColumn});
  7702. YAHOO.log("Column \"" + oColumn.key + "\" unhighlighted", "info", this.toString());
  7703. }
  7704. else {
  7705. YAHOO.log("Could not unhighlight Column \"" + oColumn.key + "\". Only non-nested Columns can be unhighlighted", "warn", this.toString());
  7706. }
  7707. },
  7708. // ROW FUNCTIONS
  7709. /**
  7710. * Adds one new Record of data into the RecordSet at the index if given,
  7711. * otherwise at the end. If the new Record is in page view, the
  7712. * corresponding DOM elements are also updated.
  7713. *
  7714. * @method addRow
  7715. * @param oData {Object} Object literal of data for the row.
  7716. * @param index {Number} (optional) RecordSet position index at which to add data.
  7717. */
  7718. addRow : function(oData, index) {
  7719. if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
  7720. YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString());
  7721. return;
  7722. }
  7723. if(oData && lang.isObject(oData)) {
  7724. var oRecord = this._oRecordSet.addRecord(oData, index);
  7725. if(oRecord) {
  7726. var recIndex;
  7727. var oPaginator = this.get('paginator');
  7728. // Paginated
  7729. if (oPaginator) {
  7730. // Update the paginator's totalRecords
  7731. var totalRecords = oPaginator.get('totalRecords');
  7732. if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
  7733. oPaginator.set('totalRecords',totalRecords + 1);
  7734. }
  7735. recIndex = this.getRecordIndex(oRecord);
  7736. var endRecIndex = (oPaginator.getPageRecords())[1];
  7737. // New record affects the view
  7738. if (recIndex <= endRecIndex) {
  7739. // Defer UI updates to the render method
  7740. this.render();
  7741. }
  7742. this.fireEvent("rowAddEvent", {record:oRecord});
  7743. YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString());
  7744. return;
  7745. }
  7746. // Not paginated
  7747. else {
  7748. recIndex = this.getTrIndex(oRecord);
  7749. if(lang.isNumber(recIndex)) {
  7750. // Add the TR element
  7751. this._oChainRender.add({
  7752. method: function(oArg) {
  7753. if((this instanceof DT) && this._sId) {
  7754. var oRecord = oArg.record;
  7755. var recIndex = oArg.recIndex;
  7756. var elNewTr = this._addTrEl(oRecord);
  7757. if(elNewTr) {
  7758. var elNext = (this._elTbody.rows[recIndex]) ? this._elTbody.rows[recIndex] : null;
  7759. this._elTbody.insertBefore(elNewTr, elNext);
  7760. // Set FIRST/LAST
  7761. if(recIndex === 0) {
  7762. this._setFirstRow();
  7763. }
  7764. if(elNext === null) {
  7765. this._setLastRow();
  7766. }
  7767. // Set EVEN/ODD
  7768. this._setRowStripes();
  7769. this.hideTableMessage();
  7770. this.fireEvent("rowAddEvent", {record:oRecord});
  7771. YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString());
  7772. }
  7773. }
  7774. },
  7775. argument: {record: oRecord, recIndex: recIndex},
  7776. scope: this,
  7777. timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
  7778. });
  7779. this._runRenderChain();
  7780. return;
  7781. }
  7782. }
  7783. }
  7784. }
  7785. YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString());
  7786. },
  7787. /**
  7788. * Convenience method to add multiple rows.
  7789. *
  7790. * @method addRows
  7791. * @param aData {Object[]} Array of object literal data for the rows.
  7792. * @param index {Number} (optional) RecordSet position index at which to add data.
  7793. */
  7794. addRows : function(aData, index) {
  7795. if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
  7796. YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString());
  7797. return;
  7798. }
  7799. if(lang.isArray(aData)) {
  7800. var aRecords = this._oRecordSet.addRecords(aData, index);
  7801. if(aRecords) {
  7802. var recIndex = this.getRecordIndex(aRecords[0]);
  7803. // Paginated
  7804. var oPaginator = this.get('paginator');
  7805. if (oPaginator) {
  7806. // Update the paginator's totalRecords
  7807. var totalRecords = oPaginator.get('totalRecords');
  7808. if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
  7809. oPaginator.set('totalRecords',totalRecords + aRecords.length);
  7810. }
  7811. var endRecIndex = (oPaginator.getPageRecords())[1];
  7812. // At least one of the new records affects the view
  7813. if (recIndex <= endRecIndex) {
  7814. this.render();
  7815. }
  7816. this.fireEvent("rowsAddEvent", {records:aRecords});
  7817. YAHOO.log("Added " + aRecords.length +
  7818. " rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) +
  7819. " with data " + lang.dump(aData), "info", this.toString());
  7820. return;
  7821. }
  7822. // Not paginated
  7823. else {
  7824. // Add the TR elements
  7825. var loopN = this.get("renderLoopSize");
  7826. var loopEnd = recIndex + aData.length;
  7827. var nRowsNeeded = (loopEnd - recIndex); // how many needed
  7828. var isLast = (recIndex >= this._elTbody.rows.length);
  7829. this._oChainRender.add({
  7830. method: function(oArg) {
  7831. if((this instanceof DT) && this._sId) {
  7832. var aRecords = oArg.aRecords,
  7833. i = oArg.nCurrentRow,
  7834. j = oArg.nCurrentRecord,
  7835. len = loopN > 0 ? Math.min(i + loopN,loopEnd) : loopEnd,
  7836. df = document.createDocumentFragment(),
  7837. elNext = (this._elTbody.rows[i]) ? this._elTbody.rows[i] : null;
  7838. for(; i < len; i++, j++) {
  7839. df.appendChild(this._addTrEl(aRecords[j]));
  7840. }
  7841. this._elTbody.insertBefore(df, elNext);
  7842. oArg.nCurrentRow = i;
  7843. oArg.nCurrentRecord = j;
  7844. }
  7845. },
  7846. iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
  7847. argument: {nCurrentRow:recIndex,nCurrentRecord:0,aRecords:aRecords},
  7848. scope: this,
  7849. timeout: (loopN > 0) ? 0 : -1
  7850. });
  7851. this._oChainRender.add({
  7852. method: function(oArg) {
  7853. var recIndex = oArg.recIndex;
  7854. // Set FIRST/LAST
  7855. if(recIndex === 0) {
  7856. this._setFirstRow();
  7857. }
  7858. if(oArg.isLast) {
  7859. this._setLastRow();
  7860. }
  7861. // Set EVEN/ODD
  7862. this._setRowStripes();
  7863. this.fireEvent("rowsAddEvent", {records:aRecords});
  7864. YAHOO.log("Added " + aRecords.length +
  7865. " rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) +
  7866. " with data " + lang.dump(aData), "info", this.toString());
  7867. },
  7868. argument: {recIndex: recIndex, isLast: isLast},
  7869. scope: this,
  7870. timeout: -1 // Needs to run immediately after the DOM insertions above
  7871. });
  7872. this._runRenderChain();
  7873. this.hideTableMessage();
  7874. return;
  7875. }
  7876. }
  7877. }
  7878. YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString());
  7879. },
  7880. /**
  7881. * For the given row, updates the associated Record with the given data. If the
  7882. * row is on current page, the corresponding DOM elements are also updated.
  7883. *
  7884. * @method updateRow
  7885. * @param row {YAHOO.widget.Record | Number | HTMLElement | String}
  7886. * Which row to update: By Record instance, by Record's RecordSet
  7887. * position index, by HTMLElement reference to the TR element, or by ID string
  7888. * of the TR element.
  7889. * @param oData {Object} Object literal of data for the row.
  7890. */
  7891. updateRow : function(row, oData) {
  7892. var index = row;
  7893. if (!lang.isNumber(index)) {
  7894. index = this.getRecordIndex(row);
  7895. }
  7896. // Update the Record
  7897. if(lang.isNumber(index) && (index >= 0)) {
  7898. var oRecordSet = this._oRecordSet,
  7899. oldRecord = oRecordSet.getRecord(index);
  7900. if(oldRecord) {
  7901. var updatedRecord = this._oRecordSet.setRecord(oData, index),
  7902. elRow = this.getTrEl(oldRecord),
  7903. // Copy data from the Record for the event that gets fired later
  7904. oldData = oldRecord ? oldRecord.getData() : null;
  7905. if(updatedRecord) {
  7906. // Update selected rows as necessary
  7907. var tracker = this._aSelections || [],
  7908. i=0,
  7909. oldId = oldRecord.getId(),
  7910. newId = updatedRecord.getId();
  7911. for(; i<tracker.length; i++) {
  7912. if((tracker[i] === oldId)) {
  7913. tracker[i] = newId;
  7914. }
  7915. else if(tracker[i].recordId === oldId) {
  7916. tracker[i].recordId = newId;
  7917. }
  7918. }
  7919. // Update the TR only if row is on current page
  7920. this._oChainRender.add({
  7921. method: function() {
  7922. if((this instanceof DT) && this._sId) {
  7923. // Paginated
  7924. var oPaginator = this.get('paginator');
  7925. if (oPaginator) {
  7926. var pageStartIndex = (oPaginator.getPageRecords())[0],
  7927. pageLastIndex = (oPaginator.getPageRecords())[1];
  7928. // At least one of the new records affects the view
  7929. if ((index >= pageStartIndex) || (index <= pageLastIndex)) {
  7930. this.render();
  7931. }
  7932. }
  7933. else {
  7934. if(elRow) {
  7935. this._updateTrEl(elRow, updatedRecord);
  7936. }
  7937. else {
  7938. this.getTbodyEl().appendChild(this._addTrEl(updatedRecord));
  7939. }
  7940. }
  7941. this.fireEvent("rowUpdateEvent", {record:updatedRecord, oldData:oldData});
  7942. YAHOO.log("DataTable row updated: Record ID = " + updatedRecord.getId() +
  7943. ", Record index = " + this.getRecordIndex(updatedRecord) +
  7944. ", page row index = " + this.getTrIndex(updatedRecord), "info", this.toString());
  7945. }
  7946. },
  7947. scope: this,
  7948. timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
  7949. });
  7950. this._runRenderChain();
  7951. return;
  7952. }
  7953. }
  7954. }
  7955. YAHOO.log("Could not update row " + row + " with the data : " + lang.dump(oData), "warn", this.toString());
  7956. return;
  7957. },
  7958. /**
  7959. * Starting with the given row, updates associated Records with the given data.
  7960. * The number of rows to update are determined by the array of data provided.
  7961. * Undefined data (i.e., not an object literal) causes a row to be skipped. If
  7962. * any of the rows are on current page, the corresponding DOM elements are also
  7963. * updated.
  7964. *
  7965. * @method updateRows
  7966. * @param startrow {YAHOO.widget.Record | Number | HTMLElement | String}
  7967. * Starting row to update: By Record instance, by Record's RecordSet
  7968. * position index, by HTMLElement reference to the TR element, or by ID string
  7969. * of the TR element.
  7970. * @param aData {Object[]} Array of object literal of data for the rows.
  7971. */
  7972. updateRows : function(startrow, aData) {
  7973. if(lang.isArray(aData)) {
  7974. var startIndex = startrow,
  7975. oRecordSet = this._oRecordSet;
  7976. if (!lang.isNumber(startrow)) {
  7977. startIndex = this.getRecordIndex(startrow);
  7978. }
  7979. if(lang.isNumber(startIndex) && (startIndex >= 0) && (startIndex < oRecordSet.getLength())) {
  7980. var lastIndex = startIndex + aData.length,
  7981. aOldRecords = oRecordSet.getRecords(startIndex, aData.length),
  7982. aNewRecords = oRecordSet.setRecords(aData, startIndex);
  7983. if(aNewRecords) {
  7984. // Update selected rows as necessary
  7985. var tracker = this._aSelections || [],
  7986. i=0, j, newId, oldId;
  7987. for(; i<tracker.length; i++) {
  7988. for(j=0; j<aOldRecords.length; j++) {
  7989. oldId = aOldRecords[j].getId();
  7990. if((tracker[i] === oldId)) {
  7991. tracker[i] = aNewRecords[j].getId();
  7992. }
  7993. else if(tracker[i].recordId === oldId) {
  7994. tracker[i].recordId = aNewRecords[j].getId();
  7995. }
  7996. }
  7997. }
  7998. // Paginated
  7999. var oPaginator = this.get('paginator');
  8000. if (oPaginator) {
  8001. var pageStartIndex = (oPaginator.getPageRecords())[0],
  8002. pageLastIndex = (oPaginator.getPageRecords())[1];
  8003. // At least one of the new records affects the view
  8004. if ((startIndex >= pageStartIndex) || (lastIndex <= pageLastIndex)) {
  8005. this.render();
  8006. }
  8007. this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
  8008. YAHOO.log("Added " + aNewRecords.length +
  8009. " rows starting at index " + startIndex +
  8010. " with data " + lang.dump(aData), "info", this.toString());
  8011. return;
  8012. }
  8013. // Not paginated
  8014. else {
  8015. // Update the TR elements
  8016. var loopN = this.get("renderLoopSize"),
  8017. rowCount = aData.length, // how many needed
  8018. lastRowIndex = this._elTbody.rows.length,
  8019. isLast = (lastIndex >= lastRowIndex),
  8020. isAdding = (lastIndex > lastRowIndex);
  8021. this._oChainRender.add({
  8022. method: function(oArg) {
  8023. if((this instanceof DT) && this._sId) {
  8024. var aRecords = oArg.aRecords,
  8025. i = oArg.nCurrentRow,
  8026. j = oArg.nDataPointer,
  8027. len = loopN > 0 ? Math.min(i+loopN, startIndex+aRecords.length) : startIndex+aRecords.length;
  8028. for(; i < len; i++,j++) {
  8029. if(isAdding && (i>=lastRowIndex)) {
  8030. this._elTbody.appendChild(this._addTrEl(aRecords[j]));
  8031. }
  8032. else {
  8033. this._updateTrEl(this._elTbody.rows[i], aRecords[j]);
  8034. }
  8035. }
  8036. oArg.nCurrentRow = i;
  8037. oArg.nDataPointer = j;
  8038. }
  8039. },
  8040. iterations: (loopN > 0) ? Math.ceil(rowCount/loopN) : 1,
  8041. argument: {nCurrentRow:startIndex,aRecords:aNewRecords,nDataPointer:0,isAdding:isAdding},
  8042. scope: this,
  8043. timeout: (loopN > 0) ? 0 : -1
  8044. });
  8045. this._oChainRender.add({
  8046. method: function(oArg) {
  8047. var recIndex = oArg.recIndex;
  8048. // Set FIRST/LAST
  8049. if(recIndex === 0) {
  8050. this._setFirstRow();
  8051. }
  8052. if(oArg.isLast) {
  8053. this._setLastRow();
  8054. }
  8055. // Set EVEN/ODD
  8056. this._setRowStripes();
  8057. this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
  8058. YAHOO.log("Added " + aNewRecords.length +
  8059. " rows starting at index " + startIndex +
  8060. " with data " + lang.dump(aData), "info", this.toString());
  8061. },
  8062. argument: {recIndex: startIndex, isLast: isLast},
  8063. scope: this,
  8064. timeout: -1 // Needs to run immediately after the DOM insertions above
  8065. });
  8066. this._runRenderChain();
  8067. this.hideTableMessage();
  8068. return;
  8069. }
  8070. }
  8071. }
  8072. }
  8073. YAHOO.log("Could not update rows at " + startrow + " with " + lang.dump(aData), "warn", this.toString());
  8074. },
  8075. /**
  8076. * Deletes the given row's Record from the RecordSet. If the row is on current page,
  8077. * the corresponding DOM elements are also deleted.
  8078. *
  8079. * @method deleteRow
  8080. * @param row {HTMLElement | String | Number} DOM element reference or ID string
  8081. * to DataTable page element or RecordSet index.
  8082. */
  8083. deleteRow : function(row) {
  8084. var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
  8085. if(lang.isNumber(nRecordIndex)) {
  8086. var oRecord = this.getRecord(nRecordIndex);
  8087. if(oRecord) {
  8088. var nTrIndex = this.getTrIndex(nRecordIndex);
  8089. // Remove from selection tracker if there
  8090. var sRecordId = oRecord.getId();
  8091. var tracker = this._aSelections || [];
  8092. for(var j=tracker.length-1; j>-1; j--) {
  8093. if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
  8094. (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
  8095. tracker.splice(j,1);
  8096. }
  8097. }
  8098. // Delete Record from RecordSet
  8099. var oData = this._oRecordSet.deleteRecord(nRecordIndex);
  8100. // Update the UI
  8101. if(oData) {
  8102. // If paginated and the deleted row was on this or a prior page, just
  8103. // re-render
  8104. var oPaginator = this.get('paginator');
  8105. if (oPaginator) {
  8106. // Update the paginator's totalRecords
  8107. var totalRecords = oPaginator.get('totalRecords'),
  8108. // must capture before the totalRecords change because
  8109. // Paginator shifts to previous page automatically
  8110. rng = oPaginator.getPageRecords();
  8111. if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
  8112. oPaginator.set('totalRecords',totalRecords - 1);
  8113. }
  8114. // The deleted record was on this or a prior page, re-render
  8115. if (!rng || nRecordIndex <= rng[1]) {
  8116. this.render();
  8117. }
  8118. this._oChainRender.add({
  8119. method: function() {
  8120. if((this instanceof DT) && this._sId) {
  8121. this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex, oldData:oData, trElIndex:nTrIndex});
  8122. YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString());
  8123. }
  8124. },
  8125. scope: this,
  8126. timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
  8127. });
  8128. this._runRenderChain();
  8129. }
  8130. // Not paginated
  8131. else {
  8132. if(lang.isNumber(nTrIndex)) {
  8133. this._oChainRender.add({
  8134. method: function() {
  8135. if((this instanceof DT) && this._sId) {
  8136. var isLast = (nRecordIndex === this._oRecordSet.getLength());//(nTrIndex == this.getLastTrEl().sectionRowIndex);
  8137. this._deleteTrEl(nTrIndex);
  8138. // Post-delete tasks
  8139. if(this._elTbody.rows.length > 0) {
  8140. // Set FIRST/LAST
  8141. if(nTrIndex === 0) {
  8142. this._setFirstRow();
  8143. }
  8144. if(isLast) {
  8145. this._setLastRow();
  8146. }
  8147. // Set EVEN/ODD
  8148. if(nTrIndex != this._elTbody.rows.length) {
  8149. this._setRowStripes(nTrIndex);
  8150. }
  8151. }
  8152. this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,oldData:oData, trElIndex:nTrIndex});
  8153. YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString());
  8154. }
  8155. },
  8156. scope: this,
  8157. timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
  8158. });
  8159. this._runRenderChain();
  8160. return;
  8161. }
  8162. }
  8163. }
  8164. }
  8165. }
  8166. YAHOO.log("Could not delete row: " + row, "warn", this.toString());
  8167. return null;
  8168. },
  8169. /**
  8170. * Convenience method to delete multiple rows.
  8171. *
  8172. * @method deleteRows
  8173. * @param row {HTMLElement | String | Number} DOM element reference or ID string
  8174. * to DataTable page element or RecordSet index.
  8175. * @param count {Number} (optional) How many rows to delete. A negative value
  8176. * will delete towards the beginning.
  8177. */
  8178. deleteRows : function(row, count) {
  8179. var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
  8180. if(lang.isNumber(nRecordIndex)) {
  8181. var oRecord = this.getRecord(nRecordIndex);
  8182. if(oRecord) {
  8183. var nTrIndex = this.getTrIndex(nRecordIndex);
  8184. // Remove from selection tracker if there
  8185. var sRecordId = oRecord.getId();
  8186. var tracker = this._aSelections || [];
  8187. for(var j=tracker.length-1; j>-1; j--) {
  8188. if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
  8189. (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
  8190. tracker.splice(j,1);
  8191. }
  8192. }
  8193. // Delete Record from RecordSet
  8194. var highIndex = nRecordIndex;
  8195. var lowIndex = nRecordIndex;
  8196. // Validate count and account for negative value
  8197. if(count && lang.isNumber(count)) {
  8198. highIndex = (count > 0) ? nRecordIndex + count -1 : nRecordIndex;
  8199. lowIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1;
  8200. count = (count > 0) ? count : count*-1;
  8201. if(lowIndex < 0) {
  8202. lowIndex = 0;
  8203. count = highIndex - lowIndex + 1;
  8204. }
  8205. }
  8206. else {
  8207. count = 1;
  8208. }
  8209. var aData = this._oRecordSet.deleteRecords(lowIndex, count);
  8210. // Update the UI
  8211. if(aData) {
  8212. var oPaginator = this.get('paginator'),
  8213. loopN = this.get("renderLoopSize");
  8214. // If paginated and the deleted row was on this or a prior page, just
  8215. // re-render
  8216. if (oPaginator) {
  8217. // Update the paginator's totalRecords
  8218. var totalRecords = oPaginator.get('totalRecords'),
  8219. // must capture before the totalRecords change because
  8220. // Paginator shifts to previous page automatically
  8221. rng = oPaginator.getPageRecords();
  8222. if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
  8223. oPaginator.set('totalRecords',totalRecords - aData.length);
  8224. }
  8225. // The records were on this or a prior page, re-render
  8226. if (!rng || lowIndex <= rng[1]) {
  8227. this.render();
  8228. }
  8229. this._oChainRender.add({
  8230. method: function(oArg) {
  8231. if((this instanceof DT) && this._sId) {
  8232. this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
  8233. YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString());
  8234. }
  8235. },
  8236. scope: this,
  8237. timeout: (loopN > 0) ? 0 : -1
  8238. });
  8239. this._runRenderChain();
  8240. return;
  8241. }
  8242. // Not paginated
  8243. else {
  8244. if(lang.isNumber(nTrIndex)) {
  8245. // Delete the TR elements starting with highest index
  8246. var loopEnd = lowIndex;
  8247. var nRowsNeeded = count; // how many needed
  8248. this._oChainRender.add({
  8249. method: function(oArg) {
  8250. if((this instanceof DT) && this._sId) {
  8251. var i = oArg.nCurrentRow,
  8252. len = (loopN > 0) ? (Math.max(i - loopN,loopEnd)-1) : loopEnd-1;
  8253. for(; i>len; --i) {
  8254. this._deleteTrEl(i);
  8255. }
  8256. oArg.nCurrentRow = i;
  8257. }
  8258. },
  8259. iterations: (loopN > 0) ? Math.ceil(count/loopN) : 1,
  8260. argument: {nCurrentRow:highIndex},
  8261. scope: this,
  8262. timeout: (loopN > 0) ? 0 : -1
  8263. });
  8264. this._oChainRender.add({
  8265. method: function() {
  8266. // Post-delete tasks
  8267. if(this._elTbody.rows.length > 0) {
  8268. this._setFirstRow();
  8269. this._setLastRow();
  8270. this._setRowStripes();
  8271. }
  8272. this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
  8273. YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString());
  8274. },
  8275. scope: this,
  8276. timeout: -1 // Needs to run immediately after the DOM deletions above
  8277. });
  8278. this._runRenderChain();
  8279. return;
  8280. }
  8281. }
  8282. }
  8283. }
  8284. }
  8285. YAHOO.log("Could not delete " + count + " rows at row " + row, "warn", this.toString());
  8286. return null;
  8287. },
  8288. // CELL FUNCTIONS
  8289. /**
  8290. * Outputs markup into the given TD based on given Record.
  8291. *
  8292. * @method formatCell
  8293. * @param elLiner {HTMLElement} The liner DIV element within the TD.
  8294. * @param oRecord {YAHOO.widget.Record} (Optional) Record instance.
  8295. * @param oColumn {YAHOO.widget.Column} (Optional) Column instance.
  8296. */
  8297. formatCell : function(elLiner, oRecord, oColumn) {
  8298. if(!oRecord) {
  8299. oRecord = this.getRecord(elLiner);
  8300. }
  8301. if(!oColumn) {
  8302. oColumn = this.getColumn(elLiner.parentNode.cellIndex);
  8303. }
  8304. if(oRecord && oColumn) {
  8305. var sField = oColumn.field;
  8306. var oData = oRecord.getData(sField);
  8307. var fnFormatter = typeof oColumn.formatter === 'function' ?
  8308. oColumn.formatter :
  8309. DT.Formatter[oColumn.formatter+''] ||
  8310. DT.Formatter.defaultFormatter;
  8311. // Apply special formatter
  8312. if(fnFormatter) {
  8313. fnFormatter.call(this, elLiner, oRecord, oColumn, oData);
  8314. }
  8315. else {
  8316. elLiner.innerHTML = oData;
  8317. }
  8318. this.fireEvent("cellFormatEvent", {record:oRecord, column:oColumn, key:oColumn.key, el:elLiner});
  8319. }
  8320. else {
  8321. YAHOO.log("Could not format cell " + elLiner, "error", this.toString());
  8322. }
  8323. },
  8324. /**
  8325. * For the given row and column, updates the Record with the given data. If the
  8326. * cell is on current page, the corresponding DOM elements are also updated.
  8327. *
  8328. * @method updateCell
  8329. * @param oRecord {YAHOO.widget.Record} Record instance.
  8330. * @param oColumn {YAHOO.widget.Column | String | Number} A Column key, or a ColumnSet key index.
  8331. * @param oData {Object} New data value for the cell.
  8332. */
  8333. updateCell : function(oRecord, oColumn, oData) {
  8334. // Validate Column and Record
  8335. oColumn = (oColumn instanceof YAHOO.widget.Column) ? oColumn : this.getColumn(oColumn);
  8336. if(oColumn && oColumn.getField() && (oRecord instanceof YAHOO.widget.Record)) {
  8337. var sKey = oColumn.getField(),
  8338. // Copy data from the Record for the event that gets fired later
  8339. //var oldData = YAHOO.widget.DataTable._cloneObject(oRecord.getData());
  8340. oldData = oRecord.getData(sKey);
  8341. // Update Record with new data
  8342. this._oRecordSet.updateRecordValue(oRecord, sKey, oData);
  8343. // Update the TD only if row is on current page
  8344. var elTd = this.getTdEl({record: oRecord, column: oColumn});
  8345. if(elTd) {
  8346. this._oChainRender.add({
  8347. method: function() {
  8348. if((this instanceof DT) && this._sId) {
  8349. this.formatCell(elTd.firstChild);
  8350. this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
  8351. YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() +
  8352. ", Record index = " + this.getRecordIndex(oRecord) +
  8353. ", page row index = " + this.getTrIndex(oRecord) +
  8354. ", Column key = " + oColumn.getKey(), "info", this.toString());
  8355. }
  8356. },
  8357. scope: this,
  8358. timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
  8359. });
  8360. this._runRenderChain();
  8361. }
  8362. else {
  8363. this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
  8364. YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() +
  8365. ", Record index = " + this.getRecordIndex(oRecord) +
  8366. ", page row index = " + this.getTrIndex(oRecord) +
  8367. ", Column key = " + oColumn.getKey(), "info", this.toString());
  8368. }
  8369. }
  8370. },
  8371. // PAGINATION
  8372. /**
  8373. * Method executed during set() operation for the "paginator" attribute.
  8374. * Adds and/or severs event listeners between DataTable and Paginator
  8375. *
  8376. * @method _updatePaginator
  8377. * @param newPag {Paginator} Paginator instance (or null) for DataTable to use
  8378. * @private
  8379. */
  8380. _updatePaginator : function (newPag) {
  8381. var oldPag = this.get('paginator');
  8382. if (oldPag && newPag !== oldPag) {
  8383. oldPag.unsubscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
  8384. }
  8385. if (newPag) {
  8386. newPag.subscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
  8387. }
  8388. },
  8389. /**
  8390. * Update the UI infrastructure in response to a "paginator" attribute change.
  8391. *
  8392. * @method _handlePaginatorChange
  8393. * @param e {Object} Change event object containing keys 'type','newValue',
  8394. * and 'prevValue'
  8395. * @private
  8396. */
  8397. _handlePaginatorChange : function (e) {
  8398. if (e.prevValue === e.newValue) { return; }
  8399. var newPag = e.newValue,
  8400. oldPag = e.prevValue,
  8401. containers = this._defaultPaginatorContainers();
  8402. if (oldPag) {
  8403. if (oldPag.getContainerNodes()[0] == containers[0]) {
  8404. oldPag.set('containers',[]);
  8405. }
  8406. oldPag.destroy();
  8407. // Convenience: share the default containers if possible.
  8408. // Otherwise, remove the default containers from the DOM.
  8409. if (containers[0]) {
  8410. if (newPag && !newPag.getContainerNodes().length) {
  8411. newPag.set('containers',containers);
  8412. } else {
  8413. // No new Paginator to use existing containers, OR new
  8414. // Paginator has configured containers.
  8415. for (var i = containers.length - 1; i >= 0; --i) {
  8416. if (containers[i]) {
  8417. containers[i].parentNode.removeChild(containers[i]);
  8418. }
  8419. }
  8420. }
  8421. }
  8422. }
  8423. if (!this._bInit) {
  8424. this.render();
  8425. }
  8426. if (newPag) {
  8427. this.renderPaginator();
  8428. }
  8429. },
  8430. /**
  8431. * Returns the default containers used for Paginators. If create param is
  8432. * passed, the containers will be created and added to the DataTable container.
  8433. *
  8434. * @method _defaultPaginatorContainers
  8435. * @param create {boolean} Create the default containers if not found
  8436. * @private
  8437. */
  8438. _defaultPaginatorContainers : function (create) {
  8439. var above_id = this._sId + '-paginator0',
  8440. below_id = this._sId + '-paginator1',
  8441. above = Dom.get(above_id),
  8442. below = Dom.get(below_id);
  8443. if (create && (!above || !below)) {
  8444. // One above and one below the table
  8445. if (!above) {
  8446. above = document.createElement('div');
  8447. above.id = above_id;
  8448. Dom.addClass(above, DT.CLASS_PAGINATOR);
  8449. this._elContainer.insertBefore(above,this._elContainer.firstChild);
  8450. }
  8451. if (!below) {
  8452. below = document.createElement('div');
  8453. below.id = below_id;
  8454. Dom.addClass(below, DT.CLASS_PAGINATOR);
  8455. this._elContainer.appendChild(below);
  8456. }
  8457. }
  8458. return [above,below];
  8459. },
  8460. /**
  8461. * Calls Paginator's destroy() method
  8462. *
  8463. * @method _destroyPaginator
  8464. * @private
  8465. */
  8466. _destroyPaginator : function () {
  8467. var oldPag = this.get('paginator');
  8468. if (oldPag) {
  8469. oldPag.destroy();
  8470. }
  8471. },
  8472. /**
  8473. * Renders the Paginator to the DataTable UI
  8474. *
  8475. * @method renderPaginator
  8476. */
  8477. renderPaginator : function () {
  8478. var pag = this.get("paginator");
  8479. if (!pag) { return; }
  8480. // Add the containers if the Paginator is not configured with containers
  8481. if (!pag.getContainerNodes().length) {
  8482. pag.set('containers',this._defaultPaginatorContainers(true));
  8483. }
  8484. pag.render();
  8485. },
  8486. /**
  8487. * Overridable method gives implementers a hook to show loading message before
  8488. * changing Paginator value.
  8489. *
  8490. * @method doBeforePaginatorChange
  8491. * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
  8492. * @return {Boolean} Return true to continue changing Paginator value.
  8493. */
  8494. doBeforePaginatorChange : function(oPaginatorState) {
  8495. this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
  8496. return true;
  8497. },
  8498. /**
  8499. * Responds to new Pagination states. By default, updates the UI to reflect the
  8500. * new state. If "dynamicData" is true, current selections are purged before
  8501. * a request is sent to the DataSource for data for the new state (using the
  8502. * request returned by "generateRequest()").
  8503. *
  8504. * @method onPaginatorChangeRequest
  8505. * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
  8506. */
  8507. onPaginatorChangeRequest : function (oPaginatorState) {
  8508. var ok = this.doBeforePaginatorChange(oPaginatorState);
  8509. if(ok) {
  8510. // Server-side pagination
  8511. if(this.get("dynamicData")) {
  8512. // Get the current state
  8513. var oState = this.getState();
  8514. // Update pagination values
  8515. oState.pagination = oPaginatorState;
  8516. // Get the request for the new state
  8517. var request = this.get("generateRequest")(oState, this);
  8518. // Purge selections
  8519. this.unselectAllRows();
  8520. this.unselectAllCells();
  8521. // Get the new data from the server
  8522. var callback = {
  8523. success : this.onDataReturnSetRows,
  8524. failure : this.onDataReturnSetRows,
  8525. argument : oState, // Pass along the new state to the callback
  8526. scope : this
  8527. };
  8528. this._oDataSource.sendRequest(request, callback);
  8529. }
  8530. // Client-side pagination
  8531. else {
  8532. // Set the core pagination values silently (the second param)
  8533. // to avoid looping back through the changeRequest mechanism
  8534. oPaginatorState.paginator.setStartIndex(oPaginatorState.recordOffset,true);
  8535. oPaginatorState.paginator.setRowsPerPage(oPaginatorState.rowsPerPage,true);
  8536. // Update the UI
  8537. this.render();
  8538. }
  8539. }
  8540. else {
  8541. YAHOO.log("Could not change Paginator value \"" + oPaginatorState + "\"", "warn", this.toString());
  8542. }
  8543. },
  8544. // SELECTION/HIGHLIGHTING
  8545. /*
  8546. * Reference to last highlighted cell element
  8547. *
  8548. * @property _elLastHighlightedTd
  8549. * @type HTMLElement
  8550. * @private
  8551. */
  8552. _elLastHighlightedTd : null,
  8553. /*
  8554. * ID string of last highlighted row element
  8555. *
  8556. * @property _sLastHighlightedTrElId
  8557. * @type String
  8558. * @private
  8559. */
  8560. //_sLastHighlightedTrElId : null,
  8561. /**
  8562. * Array to track row selections (by sRecordId) and/or cell selections
  8563. * (by {recordId:sRecordId, columnKey:sColumnKey})
  8564. *
  8565. * @property _aSelections
  8566. * @type Object[]
  8567. * @private
  8568. */
  8569. _aSelections : null,
  8570. /**
  8571. * Record instance of the row selection anchor.
  8572. *
  8573. * @property _oAnchorRecord
  8574. * @type YAHOO.widget.Record
  8575. * @private
  8576. */
  8577. _oAnchorRecord : null,
  8578. /**
  8579. * Object literal representing cell selection anchor:
  8580. * {recordId:sRecordId, columnKey:sColumnKey}.
  8581. *
  8582. * @property _oAnchorCell
  8583. * @type Object
  8584. * @private
  8585. */
  8586. _oAnchorCell : null,
  8587. /**
  8588. * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
  8589. * from all TR elements on the page.
  8590. *
  8591. * @method _unselectAllTrEls
  8592. * @private
  8593. */
  8594. _unselectAllTrEls : function() {
  8595. var selectedRows = Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
  8596. Dom.removeClass(selectedRows, DT.CLASS_SELECTED);
  8597. },
  8598. /**
  8599. * Returns object literal of values that represent the selection trigger. Used
  8600. * to determine selection behavior resulting from a key event.
  8601. *
  8602. * @method _getSelectionTrigger
  8603. * @private
  8604. */
  8605. _getSelectionTrigger : function() {
  8606. var sMode = this.get("selectionMode");
  8607. var oTrigger = {};
  8608. var oTriggerCell, oTriggerRecord, nTriggerRecordIndex, elTriggerRow, nTriggerTrIndex;
  8609. // Cell mode
  8610. if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
  8611. oTriggerCell = this.getLastSelectedCell();
  8612. // No selected cells found
  8613. if(!oTriggerCell) {
  8614. return null;
  8615. }
  8616. else {
  8617. oTriggerRecord = this.getRecord(oTriggerCell.recordId);
  8618. nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
  8619. elTriggerRow = this.getTrEl(oTriggerRecord);
  8620. nTriggerTrIndex = this.getTrIndex(elTriggerRow);
  8621. // Selected cell not found on this page
  8622. if(nTriggerTrIndex === null) {
  8623. return null;
  8624. }
  8625. else {
  8626. oTrigger.record = oTriggerRecord;
  8627. oTrigger.recordIndex = nTriggerRecordIndex;
  8628. oTrigger.el = this.getTdEl(oTriggerCell);
  8629. oTrigger.trIndex = nTriggerTrIndex;
  8630. oTrigger.column = this.getColumn(oTriggerCell.columnKey);
  8631. oTrigger.colKeyIndex = oTrigger.column.getKeyIndex();
  8632. oTrigger.cell = oTriggerCell;
  8633. return oTrigger;
  8634. }
  8635. }
  8636. }
  8637. // Row mode
  8638. else {
  8639. oTriggerRecord = this.getLastSelectedRecord();
  8640. // No selected rows found
  8641. if(!oTriggerRecord) {
  8642. return null;
  8643. }
  8644. else {
  8645. // Selected row found, but is it on current page?
  8646. oTriggerRecord = this.getRecord(oTriggerRecord);
  8647. nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
  8648. elTriggerRow = this.getTrEl(oTriggerRecord);
  8649. nTriggerTrIndex = this.getTrIndex(elTriggerRow);
  8650. // Selected row not found on this page
  8651. if(nTriggerTrIndex === null) {
  8652. return null;
  8653. }
  8654. else {
  8655. oTrigger.record = oTriggerRecord;
  8656. oTrigger.recordIndex = nTriggerRecordIndex;
  8657. oTrigger.el = elTriggerRow;
  8658. oTrigger.trIndex = nTriggerTrIndex;
  8659. return oTrigger;
  8660. }
  8661. }
  8662. }
  8663. },
  8664. /**
  8665. * Returns object literal of values that represent the selection anchor. Used
  8666. * to determine selection behavior resulting from a user event.
  8667. *
  8668. * @method _getSelectionAnchor
  8669. * @param oTrigger {Object} (Optional) Object literal of selection trigger values
  8670. * (for key events).
  8671. * @private
  8672. */
  8673. _getSelectionAnchor : function(oTrigger) {
  8674. var sMode = this.get("selectionMode");
  8675. var oAnchor = {};
  8676. var oAnchorRecord, nAnchorRecordIndex, nAnchorTrIndex;
  8677. // Cell mode
  8678. if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
  8679. // Validate anchor cell
  8680. var oAnchorCell = this._oAnchorCell;
  8681. if(!oAnchorCell) {
  8682. if(oTrigger) {
  8683. oAnchorCell = this._oAnchorCell = oTrigger.cell;
  8684. }
  8685. else {
  8686. return null;
  8687. }
  8688. }
  8689. oAnchorRecord = this._oAnchorCell.record;
  8690. nAnchorRecordIndex = this._oRecordSet.getRecordIndex(oAnchorRecord);
  8691. nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
  8692. // If anchor cell is not on this page...
  8693. if(nAnchorTrIndex === null) {
  8694. // ...set TR index equal to top TR
  8695. if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
  8696. nAnchorTrIndex = 0;
  8697. }
  8698. // ...set TR index equal to bottom TR
  8699. else {
  8700. nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
  8701. }
  8702. }
  8703. oAnchor.record = oAnchorRecord;
  8704. oAnchor.recordIndex = nAnchorRecordIndex;
  8705. oAnchor.trIndex = nAnchorTrIndex;
  8706. oAnchor.column = this._oAnchorCell.column;
  8707. oAnchor.colKeyIndex = oAnchor.column.getKeyIndex();
  8708. oAnchor.cell = oAnchorCell;
  8709. return oAnchor;
  8710. }
  8711. // Row mode
  8712. else {
  8713. oAnchorRecord = this._oAnchorRecord;
  8714. if(!oAnchorRecord) {
  8715. if(oTrigger) {
  8716. oAnchorRecord = this._oAnchorRecord = oTrigger.record;
  8717. }
  8718. else {
  8719. return null;
  8720. }
  8721. }
  8722. nAnchorRecordIndex = this.getRecordIndex(oAnchorRecord);
  8723. nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
  8724. // If anchor row is not on this page...
  8725. if(nAnchorTrIndex === null) {
  8726. // ...set TR index equal to top TR
  8727. if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
  8728. nAnchorTrIndex = 0;
  8729. }
  8730. // ...set TR index equal to bottom TR
  8731. else {
  8732. nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
  8733. }
  8734. }
  8735. oAnchor.record = oAnchorRecord;
  8736. oAnchor.recordIndex = nAnchorRecordIndex;
  8737. oAnchor.trIndex = nAnchorTrIndex;
  8738. return oAnchor;
  8739. }
  8740. },
  8741. /**
  8742. * Determines selection behavior resulting from a mouse event when selection mode
  8743. * is set to "standard".
  8744. *
  8745. * @method _handleStandardSelectionByMouse
  8746. * @param oArgs.event {HTMLEvent} Event object.
  8747. * @param oArgs.target {HTMLElement} Target element.
  8748. * @private
  8749. */
  8750. _handleStandardSelectionByMouse : function(oArgs) {
  8751. var elTarget = oArgs.target;
  8752. // Validate target row
  8753. var elTargetRow = this.getTrEl(elTarget);
  8754. if(elTargetRow) {
  8755. var e = oArgs.event;
  8756. var bSHIFT = e.shiftKey;
  8757. var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
  8758. var oTargetRecord = this.getRecord(elTargetRow);
  8759. var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
  8760. var oAnchor = this._getSelectionAnchor();
  8761. var i;
  8762. // Both SHIFT and CTRL
  8763. if(bSHIFT && bCTRL) {
  8764. // Validate anchor
  8765. if(oAnchor) {
  8766. if(this.isSelected(oAnchor.record)) {
  8767. // Select all rows between anchor row and target row, including target row
  8768. if(oAnchor.recordIndex < nTargetRecordIndex) {
  8769. for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex; i++) {
  8770. if(!this.isSelected(i)) {
  8771. this.selectRow(i);
  8772. }
  8773. }
  8774. }
  8775. // Select all rows between target row and anchor row, including target row
  8776. else {
  8777. for(i=oAnchor.recordIndex-1; i>=nTargetRecordIndex; i--) {
  8778. if(!this.isSelected(i)) {
  8779. this.selectRow(i);
  8780. }
  8781. }
  8782. }
  8783. }
  8784. else {
  8785. // Unselect all rows between anchor row and target row
  8786. if(oAnchor.recordIndex < nTargetRecordIndex) {
  8787. for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex-1; i++) {
  8788. if(this.isSelected(i)) {
  8789. this.unselectRow(i);
  8790. }
  8791. }
  8792. }
  8793. // Unselect all rows between target row and anchor row
  8794. else {
  8795. for(i=nTargetRecordIndex+1; i<=oAnchor.recordIndex-1; i++) {
  8796. if(this.isSelected(i)) {
  8797. this.unselectRow(i);
  8798. }
  8799. }
  8800. }
  8801. // Select the target row
  8802. this.selectRow(oTargetRecord);
  8803. }
  8804. }
  8805. // Invalid anchor
  8806. else {
  8807. // Set anchor
  8808. this._oAnchorRecord = oTargetRecord;
  8809. // Toggle selection of target
  8810. if(this.isSelected(oTargetRecord)) {
  8811. this.unselectRow(oTargetRecord);
  8812. }
  8813. else {
  8814. this.selectRow(oTargetRecord);
  8815. }
  8816. }
  8817. }
  8818. // Only SHIFT
  8819. else if(bSHIFT) {
  8820. this.unselectAllRows();
  8821. // Validate anchor
  8822. if(oAnchor) {
  8823. // Select all rows between anchor row and target row,
  8824. // including the anchor row and target row
  8825. if(oAnchor.recordIndex < nTargetRecordIndex) {
  8826. for(i=oAnchor.recordIndex; i<=nTargetRecordIndex; i++) {
  8827. this.selectRow(i);
  8828. }
  8829. }
  8830. // Select all rows between target row and anchor row,
  8831. // including the target row and anchor row
  8832. else {
  8833. for(i=oAnchor.recordIndex; i>=nTargetRecordIndex; i--) {
  8834. this.selectRow(i);
  8835. }
  8836. }
  8837. }
  8838. // Invalid anchor
  8839. else {
  8840. // Set anchor
  8841. this._oAnchorRecord = oTargetRecord;
  8842. // Select target row only
  8843. this.selectRow(oTargetRecord);
  8844. }
  8845. }
  8846. // Only CTRL
  8847. else if(bCTRL) {
  8848. // Set anchor
  8849. this._oAnchorRecord = oTargetRecord;
  8850. // Toggle selection of target
  8851. if(this.isSelected(oTargetRecord)) {
  8852. this.unselectRow(oTargetRecord);
  8853. }
  8854. else {
  8855. this.selectRow(oTargetRecord);
  8856. }
  8857. }
  8858. // Neither SHIFT nor CTRL
  8859. else {
  8860. this._handleSingleSelectionByMouse(oArgs);
  8861. return;
  8862. }
  8863. }
  8864. },
  8865. /**
  8866. * Determines selection behavior resulting from a key event when selection mode
  8867. * is set to "standard".
  8868. *
  8869. * @method _handleStandardSelectionByKey
  8870. * @param e {HTMLEvent} Event object.
  8871. * @private
  8872. */
  8873. _handleStandardSelectionByKey : function(e) {
  8874. var nKey = Ev.getCharCode(e);
  8875. if((nKey == 38) || (nKey == 40)) {
  8876. var bSHIFT = e.shiftKey;
  8877. // Validate trigger
  8878. var oTrigger = this._getSelectionTrigger();
  8879. // Arrow selection only works if last selected row is on current page
  8880. if(!oTrigger) {
  8881. return null;
  8882. }
  8883. Ev.stopEvent(e);
  8884. // Validate anchor
  8885. var oAnchor = this._getSelectionAnchor(oTrigger);
  8886. // Determine which direction we're going to
  8887. if(bSHIFT) {
  8888. // Selecting down away from anchor row
  8889. if((nKey == 40) && (oAnchor.recordIndex <= oTrigger.trIndex)) {
  8890. this.selectRow(this.getNextTrEl(oTrigger.el));
  8891. }
  8892. // Selecting up away from anchor row
  8893. else if((nKey == 38) && (oAnchor.recordIndex >= oTrigger.trIndex)) {
  8894. this.selectRow(this.getPreviousTrEl(oTrigger.el));
  8895. }
  8896. // Unselect trigger
  8897. else {
  8898. this.unselectRow(oTrigger.el);
  8899. }
  8900. }
  8901. else {
  8902. this._handleSingleSelectionByKey(e);
  8903. }
  8904. }
  8905. },
  8906. /**
  8907. * Determines selection behavior resulting from a mouse event when selection mode
  8908. * is set to "single".
  8909. *
  8910. * @method _handleSingleSelectionByMouse
  8911. * @param oArgs.event {HTMLEvent} Event object.
  8912. * @param oArgs.target {HTMLElement} Target element.
  8913. * @private
  8914. */
  8915. _handleSingleSelectionByMouse : function(oArgs) {
  8916. var elTarget = oArgs.target;
  8917. // Validate target row
  8918. var elTargetRow = this.getTrEl(elTarget);
  8919. if(elTargetRow) {
  8920. var oTargetRecord = this.getRecord(elTargetRow);
  8921. // Set anchor
  8922. this._oAnchorRecord = oTargetRecord;
  8923. // Select only target
  8924. this.unselectAllRows();
  8925. this.selectRow(oTargetRecord);
  8926. }
  8927. },
  8928. /**
  8929. * Determines selection behavior resulting from a key event when selection mode
  8930. * is set to "single".
  8931. *
  8932. * @method _handleSingleSelectionByKey
  8933. * @param e {HTMLEvent} Event object.
  8934. * @private
  8935. */
  8936. _handleSingleSelectionByKey : function(e) {
  8937. var nKey = Ev.getCharCode(e);
  8938. if((nKey == 38) || (nKey == 40)) {
  8939. // Validate trigger
  8940. var oTrigger = this._getSelectionTrigger();
  8941. // Arrow selection only works if last selected row is on current page
  8942. if(!oTrigger) {
  8943. return null;
  8944. }
  8945. Ev.stopEvent(e);
  8946. // Determine the new row to select
  8947. var elNew;
  8948. if(nKey == 38) { // arrow up
  8949. elNew = this.getPreviousTrEl(oTrigger.el);
  8950. // Validate new row
  8951. if(elNew === null) {
  8952. //TODO: wrap around to last tr on current page
  8953. //elNew = this.getLastTrEl();
  8954. //TODO: wrap back to last tr of previous page
  8955. // Top row selection is sticky
  8956. elNew = this.getFirstTrEl();
  8957. }
  8958. }
  8959. else if(nKey == 40) { // arrow down
  8960. elNew = this.getNextTrEl(oTrigger.el);
  8961. // Validate new row
  8962. if(elNew === null) {
  8963. //TODO: wrap around to first tr on current page
  8964. //elNew = this.getFirstTrEl();
  8965. //TODO: wrap forward to first tr of previous page
  8966. // Bottom row selection is sticky
  8967. elNew = this.getLastTrEl();
  8968. }
  8969. }
  8970. // Unselect all rows
  8971. this.unselectAllRows();
  8972. // Select the new row
  8973. this.selectRow(elNew);
  8974. // Set new anchor
  8975. this._oAnchorRecord = this.getRecord(elNew);
  8976. }
  8977. },
  8978. /**
  8979. * Determines selection behavior resulting from a mouse event when selection mode
  8980. * is set to "cellblock".
  8981. *
  8982. * @method _handleCellBlockSelectionByMouse
  8983. * @param oArgs.event {HTMLEvent} Event object.
  8984. * @param oArgs.target {HTMLElement} Target element.
  8985. * @private
  8986. */
  8987. _handleCellBlockSelectionByMouse : function(oArgs) {
  8988. var elTarget = oArgs.target;
  8989. // Validate target cell
  8990. var elTargetCell = this.getTdEl(elTarget);
  8991. if(elTargetCell) {
  8992. var e = oArgs.event;
  8993. var bSHIFT = e.shiftKey;
  8994. var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
  8995. var elTargetRow = this.getTrEl(elTargetCell);
  8996. var nTargetTrIndex = this.getTrIndex(elTargetRow);
  8997. var oTargetColumn = this.getColumn(elTargetCell);
  8998. var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
  8999. var oTargetRecord = this.getRecord(elTargetRow);
  9000. var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
  9001. var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
  9002. var oAnchor = this._getSelectionAnchor();
  9003. var allRows = this.getTbodyEl().rows;
  9004. var startIndex, endIndex, currentRow, i, j;
  9005. // Both SHIFT and CTRL
  9006. if(bSHIFT && bCTRL) {
  9007. // Validate anchor
  9008. if(oAnchor) {
  9009. // Anchor is selected
  9010. if(this.isSelected(oAnchor.cell)) {
  9011. // All cells are on the same row
  9012. if(oAnchor.recordIndex === nTargetRecordIndex) {
  9013. // Select all cells between anchor cell and target cell, including target cell
  9014. if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
  9015. for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
  9016. this.selectCell(elTargetRow.cells[i]);
  9017. }
  9018. }
  9019. // Select all cells between target cell and anchor cell, including target cell
  9020. else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
  9021. for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
  9022. this.selectCell(elTargetRow.cells[i]);
  9023. }
  9024. }
  9025. }
  9026. // Anchor row is above target row
  9027. else if(oAnchor.recordIndex < nTargetRecordIndex) {
  9028. startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
  9029. endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
  9030. // Select all cells from startIndex to endIndex on rows between anchor row and target row
  9031. for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
  9032. for(j=startIndex; j<=endIndex; j++) {
  9033. this.selectCell(allRows[i].cells[j]);
  9034. }
  9035. }
  9036. }
  9037. // Anchor row is below target row
  9038. else {
  9039. startIndex = Math.min(oAnchor.trIndex, nTargetColKeyIndex);
  9040. endIndex = Math.max(oAnchor.trIndex, nTargetColKeyIndex);
  9041. // Select all cells from startIndex to endIndex on rows between target row and anchor row
  9042. for(i=oAnchor.trIndex; i>=nTargetTrIndex; i--) {
  9043. for(j=endIndex; j>=startIndex; j--) {
  9044. this.selectCell(allRows[i].cells[j]);
  9045. }
  9046. }
  9047. }
  9048. }
  9049. // Anchor cell is unselected
  9050. else {
  9051. // All cells are on the same row
  9052. if(oAnchor.recordIndex === nTargetRecordIndex) {
  9053. // Unselect all cells between anchor cell and target cell
  9054. if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
  9055. for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
  9056. this.unselectCell(elTargetRow.cells[i]);
  9057. }
  9058. }
  9059. // Select all cells between target cell and anchor cell
  9060. else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
  9061. for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
  9062. this.unselectCell(elTargetRow.cells[i]);
  9063. }
  9064. }
  9065. }
  9066. // Anchor row is above target row
  9067. if(oAnchor.recordIndex < nTargetRecordIndex) {
  9068. // Unselect all cells from anchor cell to target cell
  9069. for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
  9070. currentRow = allRows[i];
  9071. for(j=0; j<currentRow.cells.length; j++) {
  9072. // This is the anchor row, only unselect cells after the anchor cell
  9073. if(currentRow.sectionRowIndex === oAnchor.trIndex) {
  9074. if(j>oAnchor.colKeyIndex) {
  9075. this.unselectCell(currentRow.cells[j]);
  9076. }
  9077. }
  9078. // This is the target row, only unelect cells before the target cell
  9079. else if(currentRow.sectionRowIndex === nTargetTrIndex) {
  9080. if(j<nTargetColKeyIndex) {
  9081. this.unselectCell(currentRow.cells[j]);
  9082. }
  9083. }
  9084. // Unselect all cells on this row
  9085. else {
  9086. this.unselectCell(currentRow.cells[j]);
  9087. }
  9088. }
  9089. }
  9090. }
  9091. // Anchor row is below target row
  9092. else {
  9093. // Unselect all cells from target cell to anchor cell
  9094. for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
  9095. currentRow = allRows[i];
  9096. for(j=0; j<currentRow.cells.length; j++) {
  9097. // This is the target row, only unselect cells after the target cell
  9098. if(currentRow.sectionRowIndex == nTargetTrIndex) {
  9099. if(j>nTargetColKeyIndex) {
  9100. this.unselectCell(currentRow.cells[j]);
  9101. }
  9102. }
  9103. // This is the anchor row, only unselect cells before the anchor cell
  9104. else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
  9105. if(j<oAnchor.colKeyIndex) {
  9106. this.unselectCell(currentRow.cells[j]);
  9107. }
  9108. }
  9109. // Unselect all cells on this row
  9110. else {
  9111. this.unselectCell(currentRow.cells[j]);
  9112. }
  9113. }
  9114. }
  9115. }
  9116. // Select the target cell
  9117. this.selectCell(elTargetCell);
  9118. }
  9119. }
  9120. // Invalid anchor
  9121. else {
  9122. // Set anchor
  9123. this._oAnchorCell = oTargetCell;
  9124. // Toggle selection of target
  9125. if(this.isSelected(oTargetCell)) {
  9126. this.unselectCell(oTargetCell);
  9127. }
  9128. else {
  9129. this.selectCell(oTargetCell);
  9130. }
  9131. }
  9132. }
  9133. // Only SHIFT
  9134. else if(bSHIFT) {
  9135. this.unselectAllCells();
  9136. // Validate anchor
  9137. if(oAnchor) {
  9138. // All cells are on the same row
  9139. if(oAnchor.recordIndex === nTargetRecordIndex) {
  9140. // Select all cells between anchor cell and target cell,
  9141. // including the anchor cell and target cell
  9142. if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
  9143. for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
  9144. this.selectCell(elTargetRow.cells[i]);
  9145. }
  9146. }
  9147. // Select all cells between target cell and anchor cell
  9148. // including the target cell and anchor cell
  9149. else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
  9150. for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
  9151. this.selectCell(elTargetRow.cells[i]);
  9152. }
  9153. }
  9154. }
  9155. // Anchor row is above target row
  9156. else if(oAnchor.recordIndex < nTargetRecordIndex) {
  9157. // Select the cellblock from anchor cell to target cell
  9158. // including the anchor cell and the target cell
  9159. startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
  9160. endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
  9161. for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
  9162. for(j=startIndex; j<=endIndex; j++) {
  9163. this.selectCell(allRows[i].cells[j]);
  9164. }
  9165. }
  9166. }
  9167. // Anchor row is below target row
  9168. else {
  9169. // Select the cellblock from target cell to anchor cell
  9170. // including the target cell and the anchor cell
  9171. startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
  9172. endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
  9173. for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
  9174. for(j=startIndex; j<=endIndex; j++) {
  9175. this.selectCell(allRows[i].cells[j]);
  9176. }
  9177. }
  9178. }
  9179. }
  9180. // Invalid anchor
  9181. else {
  9182. // Set anchor
  9183. this._oAnchorCell = oTargetCell;
  9184. // Select target only
  9185. this.selectCell(oTargetCell);
  9186. }
  9187. }
  9188. // Only CTRL
  9189. else if(bCTRL) {
  9190. // Set anchor
  9191. this._oAnchorCell = oTargetCell;
  9192. // Toggle selection of target
  9193. if(this.isSelected(oTargetCell)) {
  9194. this.unselectCell(oTargetCell);
  9195. }
  9196. else {
  9197. this.selectCell(oTargetCell);
  9198. }
  9199. }
  9200. // Neither SHIFT nor CTRL
  9201. else {
  9202. this._handleSingleCellSelectionByMouse(oArgs);
  9203. }
  9204. }
  9205. },
  9206. /**
  9207. * Determines selection behavior resulting from a key event when selection mode
  9208. * is set to "cellblock".
  9209. *
  9210. * @method _handleCellBlockSelectionByKey
  9211. * @param e {HTMLEvent} Event object.
  9212. * @private
  9213. */
  9214. _handleCellBlockSelectionByKey : function(e) {
  9215. var nKey = Ev.getCharCode(e);
  9216. var bSHIFT = e.shiftKey;
  9217. if((nKey == 9) || !bSHIFT) {
  9218. this._handleSingleCellSelectionByKey(e);
  9219. return;
  9220. }
  9221. if((nKey > 36) && (nKey < 41)) {
  9222. // Validate trigger
  9223. var oTrigger = this._getSelectionTrigger();
  9224. // Arrow selection only works if last selected row is on current page
  9225. if(!oTrigger) {
  9226. return null;
  9227. }
  9228. Ev.stopEvent(e);
  9229. // Validate anchor
  9230. var oAnchor = this._getSelectionAnchor(oTrigger);
  9231. var i, startIndex, endIndex, elNew, elNewRow;
  9232. var allRows = this.getTbodyEl().rows;
  9233. var elThisRow = oTrigger.el.parentNode;
  9234. // Determine which direction we're going to
  9235. if(nKey == 40) { // arrow down
  9236. // Selecting away from anchor cell
  9237. if(oAnchor.recordIndex <= oTrigger.recordIndex) {
  9238. // Select the horiz block on the next row...
  9239. // ...making sure there is room below the trigger row
  9240. elNewRow = this.getNextTrEl(oTrigger.el);
  9241. if(elNewRow) {
  9242. startIndex = oAnchor.colKeyIndex;
  9243. endIndex = oTrigger.colKeyIndex;
  9244. // ...going left
  9245. if(startIndex > endIndex) {
  9246. for(i=startIndex; i>=endIndex; i--) {
  9247. elNew = elNewRow.cells[i];
  9248. this.selectCell(elNew);
  9249. }
  9250. }
  9251. // ... going right
  9252. else {
  9253. for(i=startIndex; i<=endIndex; i++) {
  9254. elNew = elNewRow.cells[i];
  9255. this.selectCell(elNew);
  9256. }
  9257. }
  9258. }
  9259. }
  9260. // Unselecting towards anchor cell
  9261. else {
  9262. startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
  9263. endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
  9264. // Unselect the horiz block on this row towards the next row
  9265. for(i=startIndex; i<=endIndex; i++) {
  9266. this.unselectCell(elThisRow.cells[i]);
  9267. }
  9268. }
  9269. }
  9270. // Arrow up
  9271. else if(nKey == 38) {
  9272. // Selecting away from anchor cell
  9273. if(oAnchor.recordIndex >= oTrigger.recordIndex) {
  9274. // Select the horiz block on the previous row...
  9275. // ...making sure there is room
  9276. elNewRow = this.getPreviousTrEl(oTrigger.el);
  9277. if(elNewRow) {
  9278. // Select in order from anchor to trigger...
  9279. startIndex = oAnchor.colKeyIndex;
  9280. endIndex = oTrigger.colKeyIndex;
  9281. // ...going left
  9282. if(startIndex > endIndex) {
  9283. for(i=startIndex; i>=endIndex; i--) {
  9284. elNew = elNewRow.cells[i];
  9285. this.selectCell(elNew);
  9286. }
  9287. }
  9288. // ... going right
  9289. else {
  9290. for(i=startIndex; i<=endIndex; i++) {
  9291. elNew = elNewRow.cells[i];
  9292. this.selectCell(elNew);
  9293. }
  9294. }
  9295. }
  9296. }
  9297. // Unselecting towards anchor cell
  9298. else {
  9299. startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
  9300. endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
  9301. // Unselect the horiz block on this row towards the previous row
  9302. for(i=startIndex; i<=endIndex; i++) {
  9303. this.unselectCell(elThisRow.cells[i]);
  9304. }
  9305. }
  9306. }
  9307. // Arrow right
  9308. else if(nKey == 39) {
  9309. // Selecting away from anchor cell
  9310. if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
  9311. // Select the next vert block to the right...
  9312. // ...making sure there is room
  9313. if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
  9314. // Select in order from anchor to trigger...
  9315. startIndex = oAnchor.trIndex;
  9316. endIndex = oTrigger.trIndex;
  9317. // ...going up
  9318. if(startIndex > endIndex) {
  9319. for(i=startIndex; i>=endIndex; i--) {
  9320. elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
  9321. this.selectCell(elNew);
  9322. }
  9323. }
  9324. // ... going down
  9325. else {
  9326. for(i=startIndex; i<=endIndex; i++) {
  9327. elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
  9328. this.selectCell(elNew);
  9329. }
  9330. }
  9331. }
  9332. }
  9333. // Unselecting towards anchor cell
  9334. else {
  9335. // Unselect the vert block on this column towards the right
  9336. startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
  9337. endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
  9338. for(i=startIndex; i<=endIndex; i++) {
  9339. this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
  9340. }
  9341. }
  9342. }
  9343. // Arrow left
  9344. else if(nKey == 37) {
  9345. // Selecting away from anchor cell
  9346. if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
  9347. //Select the previous vert block to the left
  9348. if(oTrigger.colKeyIndex > 0) {
  9349. // Select in order from anchor to trigger...
  9350. startIndex = oAnchor.trIndex;
  9351. endIndex = oTrigger.trIndex;
  9352. // ...going up
  9353. if(startIndex > endIndex) {
  9354. for(i=startIndex; i>=endIndex; i--) {
  9355. elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
  9356. this.selectCell(elNew);
  9357. }
  9358. }
  9359. // ... going down
  9360. else {
  9361. for(i=startIndex; i<=endIndex; i++) {
  9362. elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
  9363. this.selectCell(elNew);
  9364. }
  9365. }
  9366. }
  9367. }
  9368. // Unselecting towards anchor cell
  9369. else {
  9370. // Unselect the vert block on this column towards the left
  9371. startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
  9372. endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
  9373. for(i=startIndex; i<=endIndex; i++) {
  9374. this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
  9375. }
  9376. }
  9377. }
  9378. }
  9379. },
  9380. /**
  9381. * Determines selection behavior resulting from a mouse event when selection mode
  9382. * is set to "cellrange".
  9383. *
  9384. * @method _handleCellRangeSelectionByMouse
  9385. * @param oArgs.event {HTMLEvent} Event object.
  9386. * @param oArgs.target {HTMLElement} Target element.
  9387. * @private
  9388. */
  9389. _handleCellRangeSelectionByMouse : function(oArgs) {
  9390. var elTarget = oArgs.target;
  9391. // Validate target cell
  9392. var elTargetCell = this.getTdEl(elTarget);
  9393. if(elTargetCell) {
  9394. var e = oArgs.event;
  9395. var bSHIFT = e.shiftKey;
  9396. var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
  9397. var elTargetRow = this.getTrEl(elTargetCell);
  9398. var nTargetTrIndex = this.getTrIndex(elTargetRow);
  9399. var oTargetColumn = this.getColumn(elTargetCell);
  9400. var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
  9401. var oTargetRecord = this.getRecord(elTargetRow);
  9402. var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
  9403. var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
  9404. var oAnchor = this._getSelectionAnchor();
  9405. var allRows = this.getTbodyEl().rows;
  9406. var currentRow, i, j;
  9407. // Both SHIFT and CTRL
  9408. if(bSHIFT && bCTRL) {
  9409. // Validate anchor
  9410. if(oAnchor) {
  9411. // Anchor is selected
  9412. if(this.isSelected(oAnchor.cell)) {
  9413. // All cells are on the same row
  9414. if(oAnchor.recordIndex === nTargetRecordIndex) {
  9415. // Select all cells between anchor cell and target cell, including target cell
  9416. if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
  9417. for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
  9418. this.selectCell(elTargetRow.cells[i]);
  9419. }
  9420. }
  9421. // Select all cells between target cell and anchor cell, including target cell
  9422. else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
  9423. for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
  9424. this.selectCell(elTargetRow.cells[i]);
  9425. }
  9426. }
  9427. }
  9428. // Anchor row is above target row
  9429. else if(oAnchor.recordIndex < nTargetRecordIndex) {
  9430. // Select all cells on anchor row from anchor cell to the end of the row
  9431. for(i=oAnchor.colKeyIndex+1; i<elTargetRow.cells.length; i++) {
  9432. this.selectCell(elTargetRow.cells[i]);
  9433. }
  9434. // Select all cells on all rows between anchor row and target row
  9435. for(i=oAnchor.trIndex+1; i<nTargetTrIndex; i++) {
  9436. for(j=0; j<allRows[i].cells.length; j++){
  9437. this.selectCell(allRows[i].cells[j]);
  9438. }
  9439. }
  9440. // Select all cells on target row from first cell to the target cell
  9441. for(i=0; i<=nTargetColKeyIndex; i++) {
  9442. this.selectCell(elTargetRow.cells[i]);
  9443. }
  9444. }
  9445. // Anchor row is below target row
  9446. else {
  9447. // Select all cells on target row from target cell to the end of the row
  9448. for(i=nTargetColKeyIndex; i<elTargetRow.cells.length; i++) {
  9449. this.selectCell(elTargetRow.cells[i]);
  9450. }
  9451. // Select all cells on all rows between target row and anchor row
  9452. for(i=nTargetTrIndex+1; i<oAnchor.trIndex; i++) {
  9453. for(j=0; j<allRows[i].cells.length; j++){
  9454. this.selectCell(allRows[i].cells[j]);
  9455. }
  9456. }
  9457. // Select all cells on anchor row from first cell to the anchor cell
  9458. for(i=0; i<oAnchor.colKeyIndex; i++) {
  9459. this.selectCell(elTargetRow.cells[i]);
  9460. }
  9461. }
  9462. }
  9463. // Anchor cell is unselected
  9464. else {
  9465. // All cells are on the same row
  9466. if(oAnchor.recordIndex === nTargetRecordIndex) {
  9467. // Unselect all cells between anchor cell and target cell
  9468. if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
  9469. for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
  9470. this.unselectCell(elTargetRow.cells[i]);
  9471. }
  9472. }
  9473. // Select all cells between target cell and anchor cell
  9474. else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
  9475. for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
  9476. this.unselectCell(elTargetRow.cells[i]);
  9477. }
  9478. }
  9479. }
  9480. // Anchor row is above target row
  9481. if(oAnchor.recordIndex < nTargetRecordIndex) {
  9482. // Unselect all cells from anchor cell to target cell
  9483. for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
  9484. currentRow = allRows[i];
  9485. for(j=0; j<currentRow.cells.length; j++) {
  9486. // This is the anchor row, only unselect cells after the anchor cell
  9487. if(currentRow.sectionRowIndex === oAnchor.trIndex) {
  9488. if(j>oAnchor.colKeyIndex) {
  9489. this.unselectCell(currentRow.cells[j]);
  9490. }
  9491. }
  9492. // This is the target row, only unelect cells before the target cell
  9493. else if(currentRow.sectionRowIndex === nTargetTrIndex) {
  9494. if(j<nTargetColKeyIndex) {
  9495. this.unselectCell(currentRow.cells[j]);
  9496. }
  9497. }
  9498. // Unselect all cells on this row
  9499. else {
  9500. this.unselectCell(currentRow.cells[j]);
  9501. }
  9502. }
  9503. }
  9504. }
  9505. // Anchor row is below target row
  9506. else {
  9507. // Unselect all cells from target cell to anchor cell
  9508. for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
  9509. currentRow = allRows[i];
  9510. for(j=0; j<currentRow.cells.length; j++) {
  9511. // This is the target row, only unselect cells after the target cell
  9512. if(currentRow.sectionRowIndex == nTargetTrIndex) {
  9513. if(j>nTargetColKeyIndex) {
  9514. this.unselectCell(currentRow.cells[j]);
  9515. }
  9516. }
  9517. // This is the anchor row, only unselect cells before the anchor cell
  9518. else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
  9519. if(j<oAnchor.colKeyIndex) {
  9520. this.unselectCell(currentRow.cells[j]);
  9521. }
  9522. }
  9523. // Unselect all cells on this row
  9524. else {
  9525. this.unselectCell(currentRow.cells[j]);
  9526. }
  9527. }
  9528. }
  9529. }
  9530. // Select the target cell
  9531. this.selectCell(elTargetCell);
  9532. }
  9533. }
  9534. // Invalid anchor
  9535. else {
  9536. // Set anchor
  9537. this._oAnchorCell = oTargetCell;
  9538. // Toggle selection of target
  9539. if(this.isSelected(oTargetCell)) {
  9540. this.unselectCell(oTargetCell);
  9541. }
  9542. else {
  9543. this.selectCell(oTargetCell);
  9544. }
  9545. }
  9546. }
  9547. // Only SHIFT
  9548. else if(bSHIFT) {
  9549. this.unselectAllCells();
  9550. // Validate anchor
  9551. if(oAnchor) {
  9552. // All cells are on the same row
  9553. if(oAnchor.recordIndex === nTargetRecordIndex) {
  9554. // Select all cells between anchor cell and target cell,
  9555. // including the anchor cell and target cell
  9556. if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
  9557. for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
  9558. this.selectCell(elTargetRow.cells[i]);
  9559. }
  9560. }
  9561. // Select all cells between target cell and anchor cell
  9562. // including the target cell and anchor cell
  9563. else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
  9564. for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
  9565. this.selectCell(elTargetRow.cells[i]);
  9566. }
  9567. }
  9568. }
  9569. // Anchor row is above target row
  9570. else if(oAnchor.recordIndex < nTargetRecordIndex) {
  9571. // Select all cells from anchor cell to target cell
  9572. // including the anchor cell and target cell
  9573. for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
  9574. currentRow = allRows[i];
  9575. for(j=0; j<currentRow.cells.length; j++) {
  9576. // This is the anchor row, only select the anchor cell and after
  9577. if(currentRow.sectionRowIndex == oAnchor.trIndex) {
  9578. if(j>=oAnchor.colKeyIndex) {
  9579. this.selectCell(currentRow.cells[j]);
  9580. }
  9581. }
  9582. // This is the target row, only select the target cell and before
  9583. else if(currentRow.sectionRowIndex == nTargetTrIndex) {
  9584. if(j<=nTargetColKeyIndex) {
  9585. this.selectCell(currentRow.cells[j]);
  9586. }
  9587. }
  9588. // Select all cells on this row
  9589. else {
  9590. this.selectCell(currentRow.cells[j]);
  9591. }
  9592. }
  9593. }
  9594. }
  9595. // Anchor row is below target row
  9596. else {
  9597. // Select all cells from target cell to anchor cell,
  9598. // including the target cell and anchor cell
  9599. for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
  9600. currentRow = allRows[i];
  9601. for(j=0; j<currentRow.cells.length; j++) {
  9602. // This is the target row, only select the target cell and after
  9603. if(currentRow.sectionRowIndex == nTargetTrIndex) {
  9604. if(j>=nTargetColKeyIndex) {
  9605. this.selectCell(currentRow.cells[j]);
  9606. }
  9607. }
  9608. // This is the anchor row, only select the anchor cell and before
  9609. else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
  9610. if(j<=oAnchor.colKeyIndex) {
  9611. this.selectCell(currentRow.cells[j]);
  9612. }
  9613. }
  9614. // Select all cells on this row
  9615. else {
  9616. this.selectCell(currentRow.cells[j]);
  9617. }
  9618. }
  9619. }
  9620. }
  9621. }
  9622. // Invalid anchor
  9623. else {
  9624. // Set anchor
  9625. this._oAnchorCell = oTargetCell;
  9626. // Select target only
  9627. this.selectCell(oTargetCell);
  9628. }
  9629. }
  9630. // Only CTRL
  9631. else if(bCTRL) {
  9632. // Set anchor
  9633. this._oAnchorCell = oTargetCell;
  9634. // Toggle selection of target
  9635. if(this.isSelected(oTargetCell)) {
  9636. this.unselectCell(oTargetCell);
  9637. }
  9638. else {
  9639. this.selectCell(oTargetCell);
  9640. }
  9641. }
  9642. // Neither SHIFT nor CTRL
  9643. else {
  9644. this._handleSingleCellSelectionByMouse(oArgs);
  9645. }
  9646. }
  9647. },
  9648. /**
  9649. * Determines selection behavior resulting from a key event when selection mode
  9650. * is set to "cellrange".
  9651. *
  9652. * @method _handleCellRangeSelectionByKey
  9653. * @param e {HTMLEvent} Event object.
  9654. * @private
  9655. */
  9656. _handleCellRangeSelectionByKey : function(e) {
  9657. var nKey = Ev.getCharCode(e);
  9658. var bSHIFT = e.shiftKey;
  9659. if((nKey == 9) || !bSHIFT) {
  9660. this._handleSingleCellSelectionByKey(e);
  9661. return;
  9662. }
  9663. if((nKey > 36) && (nKey < 41)) {
  9664. // Validate trigger
  9665. var oTrigger = this._getSelectionTrigger();
  9666. // Arrow selection only works if last selected row is on current page
  9667. if(!oTrigger) {
  9668. return null;
  9669. }
  9670. Ev.stopEvent(e);
  9671. // Validate anchor
  9672. var oAnchor = this._getSelectionAnchor(oTrigger);
  9673. var i, elNewRow, elNew;
  9674. var allRows = this.getTbodyEl().rows;
  9675. var elThisRow = oTrigger.el.parentNode;
  9676. // Arrow down
  9677. if(nKey == 40) {
  9678. elNewRow = this.getNextTrEl(oTrigger.el);
  9679. // Selecting away from anchor cell
  9680. if(oAnchor.recordIndex <= oTrigger.recordIndex) {
  9681. // Select all cells to the end of this row
  9682. for(i=oTrigger.colKeyIndex+1; i<elThisRow.cells.length; i++){
  9683. elNew = elThisRow.cells[i];
  9684. this.selectCell(elNew);
  9685. }
  9686. // Select some of the cells on the next row down
  9687. if(elNewRow) {
  9688. for(i=0; i<=oTrigger.colKeyIndex; i++){
  9689. elNew = elNewRow.cells[i];
  9690. this.selectCell(elNew);
  9691. }
  9692. }
  9693. }
  9694. // Unselecting towards anchor cell
  9695. else {
  9696. // Unselect all cells to the end of this row
  9697. for(i=oTrigger.colKeyIndex; i<elThisRow.cells.length; i++){
  9698. this.unselectCell(elThisRow.cells[i]);
  9699. }
  9700. // Unselect some of the cells on the next row down
  9701. if(elNewRow) {
  9702. for(i=0; i<oTrigger.colKeyIndex; i++){
  9703. this.unselectCell(elNewRow.cells[i]);
  9704. }
  9705. }
  9706. }
  9707. }
  9708. // Arrow up
  9709. else if(nKey == 38) {
  9710. elNewRow = this.getPreviousTrEl(oTrigger.el);
  9711. // Selecting away from anchor cell
  9712. if(oAnchor.recordIndex >= oTrigger.recordIndex) {
  9713. // Select all the cells to the beginning of this row
  9714. for(i=oTrigger.colKeyIndex-1; i>-1; i--){
  9715. elNew = elThisRow.cells[i];
  9716. this.selectCell(elNew);
  9717. }
  9718. // Select some of the cells from the end of the previous row
  9719. if(elNewRow) {
  9720. for(i=elThisRow.cells.length-1; i>=oTrigger.colKeyIndex; i--){
  9721. elNew = elNewRow.cells[i];
  9722. this.selectCell(elNew);
  9723. }
  9724. }
  9725. }
  9726. // Unselecting towards anchor cell
  9727. else {
  9728. // Unselect all the cells to the beginning of this row
  9729. for(i=oTrigger.colKeyIndex; i>-1; i--){
  9730. this.unselectCell(elThisRow.cells[i]);
  9731. }
  9732. // Unselect some of the cells from the end of the previous row
  9733. if(elNewRow) {
  9734. for(i=elThisRow.cells.length-1; i>oTrigger.colKeyIndex; i--){
  9735. this.unselectCell(elNewRow.cells[i]);
  9736. }
  9737. }
  9738. }
  9739. }
  9740. // Arrow right
  9741. else if(nKey == 39) {
  9742. elNewRow = this.getNextTrEl(oTrigger.el);
  9743. // Selecting away from anchor cell
  9744. if(oAnchor.recordIndex < oTrigger.recordIndex) {
  9745. // Select the next cell to the right
  9746. if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
  9747. elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
  9748. this.selectCell(elNew);
  9749. }
  9750. // Select the first cell of the next row
  9751. else if(elNewRow) {
  9752. elNew = elNewRow.cells[0];
  9753. this.selectCell(elNew);
  9754. }
  9755. }
  9756. // Unselecting towards anchor cell
  9757. else if(oAnchor.recordIndex > oTrigger.recordIndex) {
  9758. this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
  9759. // Unselect this cell towards the right
  9760. if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
  9761. }
  9762. // Unselect this cells towards the first cell of the next row
  9763. else {
  9764. }
  9765. }
  9766. // Anchor is on this row
  9767. else {
  9768. // Selecting away from anchor
  9769. if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
  9770. // Select the next cell to the right
  9771. if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
  9772. elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
  9773. this.selectCell(elNew);
  9774. }
  9775. // Select the first cell on the next row
  9776. else if(oTrigger.trIndex < allRows.length-1){
  9777. elNew = elNewRow.cells[0];
  9778. this.selectCell(elNew);
  9779. }
  9780. }
  9781. // Unselecting towards anchor
  9782. else {
  9783. // Unselect this cell towards the right
  9784. this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
  9785. }
  9786. }
  9787. }
  9788. // Arrow left
  9789. else if(nKey == 37) {
  9790. elNewRow = this.getPreviousTrEl(oTrigger.el);
  9791. // Unselecting towards the anchor
  9792. if(oAnchor.recordIndex < oTrigger.recordIndex) {
  9793. this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
  9794. // Unselect this cell towards the left
  9795. if(oTrigger.colKeyIndex > 0) {
  9796. }
  9797. // Unselect this cell towards the last cell of the previous row
  9798. else {
  9799. }
  9800. }
  9801. // Selecting towards the anchor
  9802. else if(oAnchor.recordIndex > oTrigger.recordIndex) {
  9803. // Select the next cell to the left
  9804. if(oTrigger.colKeyIndex > 0) {
  9805. elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
  9806. this.selectCell(elNew);
  9807. }
  9808. // Select the last cell of the previous row
  9809. else if(oTrigger.trIndex > 0){
  9810. elNew = elNewRow.cells[elNewRow.cells.length-1];
  9811. this.selectCell(elNew);
  9812. }
  9813. }
  9814. // Anchor is on this row
  9815. else {
  9816. // Selecting away from anchor cell
  9817. if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
  9818. // Select the next cell to the left
  9819. if(oTrigger.colKeyIndex > 0) {
  9820. elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
  9821. this.selectCell(elNew);
  9822. }
  9823. // Select the last cell of the previous row
  9824. else if(oTrigger.trIndex > 0){
  9825. elNew = elNewRow.cells[elNewRow.cells.length-1];
  9826. this.selectCell(elNew);
  9827. }
  9828. }
  9829. // Unselecting towards anchor cell
  9830. else {
  9831. this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
  9832. // Unselect this cell towards the left
  9833. if(oTrigger.colKeyIndex > 0) {
  9834. }
  9835. // Unselect this cell towards the last cell of the previous row
  9836. else {
  9837. }
  9838. }
  9839. }
  9840. }
  9841. }
  9842. },
  9843. /**
  9844. * Determines selection behavior resulting from a mouse event when selection mode
  9845. * is set to "singlecell".
  9846. *
  9847. * @method _handleSingleCellSelectionByMouse
  9848. * @param oArgs.event {HTMLEvent} Event object.
  9849. * @param oArgs.target {HTMLElement} Target element.
  9850. * @private
  9851. */
  9852. _handleSingleCellSelectionByMouse : function(oArgs) {
  9853. var elTarget = oArgs.target;
  9854. // Validate target cell
  9855. var elTargetCell = this.getTdEl(elTarget);
  9856. if(elTargetCell) {
  9857. var elTargetRow = this.getTrEl(elTargetCell);
  9858. var oTargetRecord = this.getRecord(elTargetRow);
  9859. var oTargetColumn = this.getColumn(elTargetCell);
  9860. var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
  9861. // Set anchor
  9862. this._oAnchorCell = oTargetCell;
  9863. // Select only target
  9864. this.unselectAllCells();
  9865. this.selectCell(oTargetCell);
  9866. }
  9867. },
  9868. /**
  9869. * Determines selection behavior resulting from a key event when selection mode
  9870. * is set to "singlecell".
  9871. *
  9872. * @method _handleSingleCellSelectionByKey
  9873. * @param e {HTMLEvent} Event object.
  9874. * @private
  9875. */
  9876. _handleSingleCellSelectionByKey : function(e) {
  9877. var nKey = Ev.getCharCode(e);
  9878. if((nKey == 9) || ((nKey > 36) && (nKey < 41))) {
  9879. var bSHIFT = e.shiftKey;
  9880. // Validate trigger
  9881. var oTrigger = this._getSelectionTrigger();
  9882. // Arrow selection only works if last selected row is on current page
  9883. if(!oTrigger) {
  9884. return null;
  9885. }
  9886. // Determine the new cell to select
  9887. var elNew;
  9888. if(nKey == 40) { // Arrow down
  9889. elNew = this.getBelowTdEl(oTrigger.el);
  9890. // Validate new cell
  9891. if(elNew === null) {
  9892. //TODO: wrap around to first tr on current page
  9893. //TODO: wrap forward to first tr of next page
  9894. // Bottom selection is sticky
  9895. elNew = oTrigger.el;
  9896. }
  9897. }
  9898. else if(nKey == 38) { // Arrow up
  9899. elNew = this.getAboveTdEl(oTrigger.el);
  9900. // Validate new cell
  9901. if(elNew === null) {
  9902. //TODO: wrap around to last tr on current page
  9903. //TODO: wrap back to last tr of previous page
  9904. // Top selection is sticky
  9905. elNew = oTrigger.el;
  9906. }
  9907. }
  9908. else if((nKey == 39) || (!bSHIFT && (nKey == 9))) { // Arrow right or tab
  9909. elNew = this.getNextTdEl(oTrigger.el);
  9910. // Validate new cell
  9911. if(elNew === null) {
  9912. //TODO: wrap around to first td on current page
  9913. //TODO: wrap forward to first td of next page
  9914. // Top-left selection is sticky, and release TAB focus
  9915. //elNew = oTrigger.el;
  9916. return;
  9917. }
  9918. }
  9919. else if((nKey == 37) || (bSHIFT && (nKey == 9))) { // Arrow left or shift-tab
  9920. elNew = this.getPreviousTdEl(oTrigger.el);
  9921. // Validate new cell
  9922. if(elNew === null) {
  9923. //TODO: wrap around to last td on current page
  9924. //TODO: wrap back to last td of previous page
  9925. // Bottom-right selection is sticky, and release TAB focus
  9926. //elNew = oTrigger.el;
  9927. return;
  9928. }
  9929. }
  9930. Ev.stopEvent(e);
  9931. // Unselect all cells
  9932. this.unselectAllCells();
  9933. // Select the new cell
  9934. this.selectCell(elNew);
  9935. // Set new anchor
  9936. this._oAnchorCell = {record:this.getRecord(elNew), column:this.getColumn(elNew)};
  9937. }
  9938. },
  9939. /**
  9940. * Returns array of selected TR elements on the page.
  9941. *
  9942. * @method getSelectedTrEls
  9943. * @return {HTMLElement[]} Array of selected TR elements.
  9944. */
  9945. getSelectedTrEls : function() {
  9946. return Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
  9947. },
  9948. /**
  9949. * Sets given row to the selected state.
  9950. *
  9951. * @method selectRow
  9952. * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
  9953. * reference or ID string, Record instance, or RecordSet position index.
  9954. */
  9955. selectRow : function(row) {
  9956. var oRecord, elRow;
  9957. if(row instanceof YAHOO.widget.Record) {
  9958. oRecord = this._oRecordSet.getRecord(row);
  9959. elRow = this.getTrEl(oRecord);
  9960. }
  9961. else if(lang.isNumber(row)) {
  9962. oRecord = this.getRecord(row);
  9963. elRow = this.getTrEl(oRecord);
  9964. }
  9965. else {
  9966. elRow = this.getTrEl(row);
  9967. oRecord = this.getRecord(elRow);
  9968. }
  9969. if(oRecord) {
  9970. // Update selection trackers
  9971. var tracker = this._aSelections || [];
  9972. var sRecordId = oRecord.getId();
  9973. var index = -1;
  9974. // Remove if already there:
  9975. // Use Array.indexOf if available...
  9976. /*if(tracker.indexOf && (tracker.indexOf(sRecordId) > -1)) {
  9977. tracker.splice(tracker.indexOf(sRecordId),1);
  9978. }*/
  9979. if(tracker.indexOf) {
  9980. index = tracker.indexOf(sRecordId);
  9981. }
  9982. // ...or do it the old-fashioned way
  9983. else {
  9984. for(var j=tracker.length-1; j>-1; j--) {
  9985. if(tracker[j] === sRecordId){
  9986. index = j;
  9987. break;
  9988. }
  9989. }
  9990. }
  9991. if(index > -1) {
  9992. tracker.splice(index,1);
  9993. }
  9994. // Add to the end
  9995. tracker.push(sRecordId);
  9996. this._aSelections = tracker;
  9997. // Update trackers
  9998. if(!this._oAnchorRecord) {
  9999. this._oAnchorRecord = oRecord;
  10000. }
  10001. // Update UI
  10002. if(elRow) {
  10003. Dom.addClass(elRow, DT.CLASS_SELECTED);
  10004. }
  10005. this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow});
  10006. YAHOO.log("Selected " + elRow, "info", this.toString());
  10007. }
  10008. else {
  10009. YAHOO.log("Could not select row " + row, "warn", this.toString());
  10010. }
  10011. },
  10012. /**
  10013. * Sets given row to the unselected state.
  10014. *
  10015. * @method unselectRow
  10016. * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
  10017. * reference or ID string, Record instance, or RecordSet position index.
  10018. */
  10019. unselectRow : function(row) {
  10020. var elRow = this.getTrEl(row);
  10021. var oRecord;
  10022. if(row instanceof YAHOO.widget.Record) {
  10023. oRecord = this._oRecordSet.getRecord(row);
  10024. }
  10025. else if(lang.isNumber(row)) {
  10026. oRecord = this.getRecord(row);
  10027. }
  10028. else {
  10029. oRecord = this.getRecord(elRow);
  10030. }
  10031. if(oRecord) {
  10032. // Update selection trackers
  10033. var tracker = this._aSelections || [];
  10034. var sRecordId = oRecord.getId();
  10035. var index = -1;
  10036. // Use Array.indexOf if available...
  10037. if(tracker.indexOf) {
  10038. index = tracker.indexOf(sRecordId);
  10039. }
  10040. // ...or do it the old-fashioned way
  10041. else {
  10042. for(var j=tracker.length-1; j>-1; j--) {
  10043. if(tracker[j] === sRecordId){
  10044. index = j;
  10045. break;
  10046. }
  10047. }
  10048. }
  10049. if(index > -1) {
  10050. // Update tracker
  10051. tracker.splice(index,1);
  10052. this._aSelections = tracker;
  10053. // Update the UI
  10054. Dom.removeClass(elRow, DT.CLASS_SELECTED);
  10055. this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow});
  10056. YAHOO.log("Unselected " + elRow, "info", this.toString());
  10057. return;
  10058. }
  10059. }
  10060. YAHOO.log("Could not unselect row " + row, "warn", this.toString());
  10061. },
  10062. /**
  10063. * Clears out all row selections.
  10064. *
  10065. * @method unselectAllRows
  10066. */
  10067. unselectAllRows : function() {
  10068. // Remove all rows from tracker
  10069. var tracker = this._aSelections || [],
  10070. recId,
  10071. removed = [];
  10072. for(var j=tracker.length-1; j>-1; j--) {
  10073. if(lang.isString(tracker[j])){
  10074. recId = tracker.splice(j,1);
  10075. removed[removed.length] = this.getRecord(lang.isArray(recId) ? recId[0] : recId);
  10076. }
  10077. }
  10078. // Update tracker
  10079. this._aSelections = tracker;
  10080. // Update UI
  10081. this._unselectAllTrEls();
  10082. this.fireEvent("unselectAllRowsEvent", {records: removed});
  10083. YAHOO.log("Unselected all rows", "info", this.toString());
  10084. },
  10085. /**
  10086. * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
  10087. * from all TD elements in the internal tracker.
  10088. *
  10089. * @method _unselectAllTdEls
  10090. * @private
  10091. */
  10092. _unselectAllTdEls : function() {
  10093. var selectedCells = Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
  10094. Dom.removeClass(selectedCells, DT.CLASS_SELECTED);
  10095. },
  10096. /**
  10097. * Returns array of selected TD elements on the page.
  10098. *
  10099. * @method getSelectedTdEls
  10100. * @return {HTMLElement[]} Array of selected TD elements.
  10101. */
  10102. getSelectedTdEls : function() {
  10103. return Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
  10104. },
  10105. /**
  10106. * Sets given cell to the selected state.
  10107. *
  10108. * @method selectCell
  10109. * @param cell {HTMLElement | String} DOM element reference or ID string
  10110. * to DataTable page element or RecordSet index.
  10111. */
  10112. selectCell : function(cell) {
  10113. //TODO: accept {record} in selectCell()
  10114. var elCell = this.getTdEl(cell);
  10115. if(elCell) {
  10116. var oRecord = this.getRecord(elCell);
  10117. var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
  10118. if(oRecord && sColumnKey) {
  10119. // Get Record ID
  10120. var tracker = this._aSelections || [];
  10121. var sRecordId = oRecord.getId();
  10122. // Remove if there
  10123. for(var j=tracker.length-1; j>-1; j--) {
  10124. if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
  10125. tracker.splice(j,1);
  10126. break;
  10127. }
  10128. }
  10129. // Add to the end
  10130. tracker.push({recordId:sRecordId, columnKey:sColumnKey});
  10131. // Update trackers
  10132. this._aSelections = tracker;
  10133. if(!this._oAnchorCell) {
  10134. this._oAnchorCell = {record:oRecord, column:this.getColumn(sColumnKey)};
  10135. }
  10136. // Update the UI
  10137. Dom.addClass(elCell, DT.CLASS_SELECTED);
  10138. this.fireEvent("cellSelectEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key: this.getColumn(elCell.cellIndex).getKey(), el:elCell});
  10139. YAHOO.log("Selected " + elCell, "info", this.toString());
  10140. return;
  10141. }
  10142. }
  10143. YAHOO.log("Could not select cell " + cell, "warn", this.toString());
  10144. },
  10145. /**
  10146. * Sets given cell to the unselected state.
  10147. *
  10148. * @method unselectCell
  10149. * @param cell {HTMLElement | String} DOM element reference or ID string
  10150. * to DataTable page element or RecordSet index.
  10151. */
  10152. unselectCell : function(cell) {
  10153. var elCell = this.getTdEl(cell);
  10154. if(elCell) {
  10155. var oRecord = this.getRecord(elCell);
  10156. var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
  10157. if(oRecord && sColumnKey) {
  10158. // Get Record ID
  10159. var tracker = this._aSelections || [];
  10160. var id = oRecord.getId();
  10161. // Is it selected?
  10162. for(var j=tracker.length-1; j>-1; j--) {
  10163. if((tracker[j].recordId === id) && (tracker[j].columnKey === sColumnKey)){
  10164. // Remove from tracker
  10165. tracker.splice(j,1);
  10166. // Update tracker
  10167. this._aSelections = tracker;
  10168. // Update the UI
  10169. Dom.removeClass(elCell, DT.CLASS_SELECTED);
  10170. this.fireEvent("cellUnselectEvent", {record:oRecord, column: this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
  10171. YAHOO.log("Unselected " + elCell, "info", this.toString());
  10172. return;
  10173. }
  10174. }
  10175. }
  10176. }
  10177. YAHOO.log("Could not unselect cell " + cell, "warn", this.toString());
  10178. },
  10179. /**
  10180. * Clears out all cell selections.
  10181. *
  10182. * @method unselectAllCells
  10183. */
  10184. unselectAllCells : function() {
  10185. // Remove all cells from tracker
  10186. var tracker = this._aSelections || [];
  10187. for(var j=tracker.length-1; j>-1; j--) {
  10188. if(lang.isObject(tracker[j])){
  10189. tracker.splice(j,1);
  10190. }
  10191. }
  10192. // Update tracker
  10193. this._aSelections = tracker;
  10194. // Update UI
  10195. this._unselectAllTdEls();
  10196. //TODO: send data to unselectAllCellsEvent handler
  10197. this.fireEvent("unselectAllCellsEvent");
  10198. YAHOO.log("Unselected all cells", "info", this.toString());
  10199. },
  10200. /**
  10201. * Returns true if given item is selected, false otherwise.
  10202. *
  10203. * @method isSelected
  10204. * @param o {String | HTMLElement | YAHOO.widget.Record | Number
  10205. * {record:YAHOO.widget.Record, column:YAHOO.widget.Column} } TR or TD element by
  10206. * reference or ID string, a Record instance, a RecordSet position index,
  10207. * or an object literal representation
  10208. * of a cell.
  10209. * @return {Boolean} True if item is selected.
  10210. */
  10211. isSelected : function(o) {
  10212. if(o && (o.ownerDocument == document)) {
  10213. return (Dom.hasClass(this.getTdEl(o),DT.CLASS_SELECTED) || Dom.hasClass(this.getTrEl(o),DT.CLASS_SELECTED));
  10214. }
  10215. else {
  10216. var oRecord, sRecordId, j;
  10217. var tracker = this._aSelections;
  10218. if(tracker && tracker.length > 0) {
  10219. // Looking for a Record?
  10220. if(o instanceof YAHOO.widget.Record) {
  10221. oRecord = o;
  10222. }
  10223. else if(lang.isNumber(o)) {
  10224. oRecord = this.getRecord(o);
  10225. }
  10226. if(oRecord) {
  10227. sRecordId = oRecord.getId();
  10228. // Is it there?
  10229. // Use Array.indexOf if available...
  10230. if(tracker.indexOf) {
  10231. if(tracker.indexOf(sRecordId) > -1) {
  10232. return true;
  10233. }
  10234. }
  10235. // ...or do it the old-fashioned way
  10236. else {
  10237. for(j=tracker.length-1; j>-1; j--) {
  10238. if(tracker[j] === sRecordId){
  10239. return true;
  10240. }
  10241. }
  10242. }
  10243. }
  10244. // Looking for a cell
  10245. else if(o.record && o.column){
  10246. sRecordId = o.record.getId();
  10247. var sColumnKey = o.column.getKey();
  10248. for(j=tracker.length-1; j>-1; j--) {
  10249. if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
  10250. return true;
  10251. }
  10252. }
  10253. }
  10254. }
  10255. }
  10256. return false;
  10257. },
  10258. /**
  10259. * Returns selected rows as an array of Record IDs.
  10260. *
  10261. * @method getSelectedRows
  10262. * @return {String[]} Array of selected rows by Record ID.
  10263. */
  10264. getSelectedRows : function() {
  10265. var aSelectedRows = [];
  10266. var tracker = this._aSelections || [];
  10267. for(var j=0; j<tracker.length; j++) {
  10268. if(lang.isString(tracker[j])){
  10269. aSelectedRows.push(tracker[j]);
  10270. }
  10271. }
  10272. return aSelectedRows;
  10273. },
  10274. /**
  10275. * Returns selected cells as an array of object literals:
  10276. * {recordId:sRecordId, columnKey:sColumnKey}.
  10277. *
  10278. * @method getSelectedCells
  10279. * @return {Object[]} Array of selected cells by Record ID and Column ID.
  10280. */
  10281. getSelectedCells : function() {
  10282. var aSelectedCells = [];
  10283. var tracker = this._aSelections || [];
  10284. for(var j=0; j<tracker.length; j++) {
  10285. if(tracker[j] && lang.isObject(tracker[j])){
  10286. aSelectedCells.push(tracker[j]);
  10287. }
  10288. }
  10289. return aSelectedCells;
  10290. },
  10291. /**
  10292. * Returns last selected Record ID.
  10293. *
  10294. * @method getLastSelectedRecord
  10295. * @return {String} Record ID of last selected row.
  10296. */
  10297. getLastSelectedRecord : function() {
  10298. var tracker = this._aSelections;
  10299. if(tracker && tracker.length > 0) {
  10300. for(var i=tracker.length-1; i>-1; i--) {
  10301. if(lang.isString(tracker[i])){
  10302. return tracker[i];
  10303. }
  10304. }
  10305. }
  10306. },
  10307. /**
  10308. * Returns last selected cell as an object literal:
  10309. * {recordId:sRecordId, columnKey:sColumnKey}.
  10310. *
  10311. * @method getLastSelectedCell
  10312. * @return {Object} Object literal representation of a cell.
  10313. */
  10314. getLastSelectedCell : function() {
  10315. var tracker = this._aSelections;
  10316. if(tracker && tracker.length > 0) {
  10317. for(var i=tracker.length-1; i>-1; i--) {
  10318. if(tracker[i].recordId && tracker[i].columnKey){
  10319. return tracker[i];
  10320. }
  10321. }
  10322. }
  10323. },
  10324. /**
  10325. * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given row.
  10326. *
  10327. * @method highlightRow
  10328. * @param row {HTMLElement | String} DOM element reference or ID string.
  10329. */
  10330. highlightRow : function(row) {
  10331. var elRow = this.getTrEl(row);
  10332. if(elRow) {
  10333. // Make sure previous row is unhighlighted
  10334. /* if(this._sLastHighlightedTrElId) {
  10335. Dom.removeClass(this._sLastHighlightedTrElId,DT.CLASS_HIGHLIGHTED);
  10336. }*/
  10337. var oRecord = this.getRecord(elRow);
  10338. Dom.addClass(elRow,DT.CLASS_HIGHLIGHTED);
  10339. //this._sLastHighlightedTrElId = elRow.id;
  10340. this.fireEvent("rowHighlightEvent", {record:oRecord, el:elRow});
  10341. YAHOO.log("Highlighted " + elRow, "info", this.toString());
  10342. return;
  10343. }
  10344. YAHOO.log("Could not highlight row " + row, "warn", this.toString());
  10345. },
  10346. /**
  10347. * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given row.
  10348. *
  10349. * @method unhighlightRow
  10350. * @param row {HTMLElement | String} DOM element reference or ID string.
  10351. */
  10352. unhighlightRow : function(row) {
  10353. var elRow = this.getTrEl(row);
  10354. if(elRow) {
  10355. var oRecord = this.getRecord(elRow);
  10356. Dom.removeClass(elRow,DT.CLASS_HIGHLIGHTED);
  10357. this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow});
  10358. YAHOO.log("Unhighlighted " + elRow, "info", this.toString());
  10359. return;
  10360. }
  10361. YAHOO.log("Could not unhighlight row " + row, "warn", this.toString());
  10362. },
  10363. /**
  10364. * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given cell.
  10365. *
  10366. * @method highlightCell
  10367. * @param cell {HTMLElement | String} DOM element reference or ID string.
  10368. */
  10369. highlightCell : function(cell) {
  10370. var elCell = this.getTdEl(cell);
  10371. if(elCell) {
  10372. // Make sure previous cell is unhighlighted
  10373. if(this._elLastHighlightedTd) {
  10374. this.unhighlightCell(this._elLastHighlightedTd);
  10375. }
  10376. var oRecord = this.getRecord(elCell);
  10377. var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
  10378. Dom.addClass(elCell,DT.CLASS_HIGHLIGHTED);
  10379. this._elLastHighlightedTd = elCell;
  10380. this.fireEvent("cellHighlightEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
  10381. YAHOO.log("Highlighted " + elCell, "info", this.toString());
  10382. return;
  10383. }
  10384. YAHOO.log("Could not highlight cell " + cell, "warn", this.toString());
  10385. },
  10386. /**
  10387. * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given cell.
  10388. *
  10389. * @method unhighlightCell
  10390. * @param cell {HTMLElement | String} DOM element reference or ID string.
  10391. */
  10392. unhighlightCell : function(cell) {
  10393. var elCell = this.getTdEl(cell);
  10394. if(elCell) {
  10395. var oRecord = this.getRecord(elCell);
  10396. Dom.removeClass(elCell,DT.CLASS_HIGHLIGHTED);
  10397. this._elLastHighlightedTd = null;
  10398. this.fireEvent("cellUnhighlightEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
  10399. YAHOO.log("Unhighlighted " + elCell, "info", this.toString());
  10400. return;
  10401. }
  10402. YAHOO.log("Could not unhighlight cell " + cell, "warn", this.toString());
  10403. },
  10404. // INLINE EDITING
  10405. /**
  10406. * Returns current CellEditor instance, or null.
  10407. * @method getCellEditor
  10408. * @return {YAHOO.widget.CellEditor} CellEditor instance.
  10409. */
  10410. getCellEditor : function() {
  10411. return this._oCellEditor;
  10412. },
  10413. /**
  10414. * Activates and shows CellEditor instance for the given cell while deactivating and
  10415. * canceling previous CellEditor. It is baked into DataTable that only one CellEditor
  10416. * can be active at any given time.
  10417. *
  10418. * @method showCellEditor
  10419. * @param elCell {HTMLElement | String} Cell to edit.
  10420. */
  10421. showCellEditor : function(elCell, oRecord, oColumn) {
  10422. // Get a particular CellEditor
  10423. elCell = this.getTdEl(elCell);
  10424. if(elCell) {
  10425. oColumn = this.getColumn(elCell);
  10426. if(oColumn && oColumn.editor) {
  10427. var oCellEditor = this._oCellEditor;
  10428. // Clean up active CellEditor
  10429. if(oCellEditor) {
  10430. if(this._oCellEditor.cancel) {
  10431. this._oCellEditor.cancel();
  10432. }
  10433. else if(oCellEditor.isActive) {
  10434. this.cancelCellEditor();
  10435. }
  10436. }
  10437. if(oColumn.editor instanceof YAHOO.widget.BaseCellEditor) {
  10438. // Get CellEditor
  10439. oCellEditor = oColumn.editor;
  10440. var ok = oCellEditor.attach(this, elCell);
  10441. if(ok) {
  10442. oCellEditor.move();
  10443. ok = this.doBeforeShowCellEditor(oCellEditor);
  10444. if(ok) {
  10445. oCellEditor.show();
  10446. this._oCellEditor = oCellEditor;
  10447. }
  10448. }
  10449. }
  10450. // Backward compatibility
  10451. else {
  10452. if(!oRecord || !(oRecord instanceof YAHOO.widget.Record)) {
  10453. oRecord = this.getRecord(elCell);
  10454. }
  10455. if(!oColumn || !(oColumn instanceof YAHOO.widget.Column)) {
  10456. oColumn = this.getColumn(elCell);
  10457. }
  10458. if(oRecord && oColumn) {
  10459. if(!this._oCellEditor || this._oCellEditor.container) {
  10460. this._initCellEditorEl();
  10461. }
  10462. // Update Editor values
  10463. oCellEditor = this._oCellEditor;
  10464. oCellEditor.cell = elCell;
  10465. oCellEditor.record = oRecord;
  10466. oCellEditor.column = oColumn;
  10467. oCellEditor.validator = (oColumn.editorOptions &&
  10468. lang.isFunction(oColumn.editorOptions.validator)) ?
  10469. oColumn.editorOptions.validator : null;
  10470. oCellEditor.value = oRecord.getData(oColumn.key);
  10471. oCellEditor.defaultValue = null;
  10472. // Move Editor
  10473. var elContainer = oCellEditor.container;
  10474. var x = Dom.getX(elCell);
  10475. var y = Dom.getY(elCell);
  10476. // SF doesn't get xy for cells in scrolling table
  10477. // when tbody display is set to block
  10478. if(isNaN(x) || isNaN(y)) {
  10479. x = elCell.offsetLeft + // cell pos relative to table
  10480. Dom.getX(this._elTbody.parentNode) - // plus table pos relative to document
  10481. this._elTbody.scrollLeft; // minus tbody scroll
  10482. y = elCell.offsetTop + // cell pos relative to table
  10483. Dom.getY(this._elTbody.parentNode) - // plus table pos relative to document
  10484. this._elTbody.scrollTop + // minus tbody scroll
  10485. this._elThead.offsetHeight; // account for fixed THEAD cells
  10486. }
  10487. elContainer.style.left = x + "px";
  10488. elContainer.style.top = y + "px";
  10489. // Hook to customize the UI
  10490. this.doBeforeShowCellEditor(this._oCellEditor);
  10491. //TODO: This is temporarily up here due so elements can be focused
  10492. // Show Editor
  10493. elContainer.style.display = "";
  10494. // Handle ESC key
  10495. Ev.addListener(elContainer, "keydown", function(e, oSelf) {
  10496. // ESC hides Cell Editor
  10497. if((e.keyCode == 27)) {
  10498. oSelf.cancelCellEditor();
  10499. oSelf.focusTbodyEl();
  10500. }
  10501. else {
  10502. oSelf.fireEvent("editorKeydownEvent", {editor:oSelf._oCellEditor, event:e});
  10503. }
  10504. }, this);
  10505. // Render Editor markup
  10506. var fnEditor;
  10507. if(lang.isString(oColumn.editor)) {
  10508. switch(oColumn.editor) {
  10509. case "checkbox":
  10510. fnEditor = DT.editCheckbox;
  10511. break;
  10512. case "date":
  10513. fnEditor = DT.editDate;
  10514. break;
  10515. case "dropdown":
  10516. fnEditor = DT.editDropdown;
  10517. break;
  10518. case "radio":
  10519. fnEditor = DT.editRadio;
  10520. break;
  10521. case "textarea":
  10522. fnEditor = DT.editTextarea;
  10523. break;
  10524. case "textbox":
  10525. fnEditor = DT.editTextbox;
  10526. break;
  10527. default:
  10528. fnEditor = null;
  10529. }
  10530. }
  10531. else if(lang.isFunction(oColumn.editor)) {
  10532. fnEditor = oColumn.editor;
  10533. }
  10534. if(fnEditor) {
  10535. // Create DOM input elements
  10536. fnEditor(this._oCellEditor, this);
  10537. // Show Save/Cancel buttons
  10538. if(!oColumn.editorOptions || !oColumn.editorOptions.disableBtns) {
  10539. this.showCellEditorBtns(elContainer);
  10540. }
  10541. oCellEditor.isActive = true;
  10542. //TODO: verify which args to pass
  10543. this.fireEvent("editorShowEvent", {editor:oCellEditor});
  10544. YAHOO.log("Cell Editor shown for " + elCell, "info", this.toString());
  10545. return;
  10546. }
  10547. }
  10548. }
  10549. }
  10550. }
  10551. },
  10552. /**
  10553. * Backward compatibility.
  10554. *
  10555. * @method _initCellEditorEl
  10556. * @private
  10557. * @deprecated
  10558. */
  10559. _initCellEditorEl : function() {
  10560. // Attach Cell Editor container element as first child of body
  10561. var elCellEditor = document.createElement("div");
  10562. elCellEditor.id = this._sId + "-celleditor";
  10563. elCellEditor.style.display = "none";
  10564. elCellEditor.tabIndex = 0;
  10565. Dom.addClass(elCellEditor, DT.CLASS_EDITOR);
  10566. var elFirstChild = Dom.getFirstChild(document.body);
  10567. if(elFirstChild) {
  10568. elCellEditor = Dom.insertBefore(elCellEditor, elFirstChild);
  10569. }
  10570. else {
  10571. elCellEditor = document.body.appendChild(elCellEditor);
  10572. }
  10573. // Internal tracker of Cell Editor values
  10574. var oCellEditor = {};
  10575. oCellEditor.container = elCellEditor;
  10576. oCellEditor.value = null;
  10577. oCellEditor.isActive = false;
  10578. this._oCellEditor = oCellEditor;
  10579. },
  10580. /**
  10581. * Overridable abstract method to customize CellEditor before showing.
  10582. *
  10583. * @method doBeforeShowCellEditor
  10584. * @param oCellEditor {YAHOO.widget.CellEditor} The CellEditor instance.
  10585. * @return {Boolean} Return true to continue showing CellEditor.
  10586. */
  10587. doBeforeShowCellEditor : function(oCellEditor) {
  10588. return true;
  10589. },
  10590. /**
  10591. * Saves active CellEditor input to Record and upates DOM UI.
  10592. *
  10593. * @method saveCellEditor
  10594. */
  10595. saveCellEditor : function() {
  10596. if(this._oCellEditor) {
  10597. if(this._oCellEditor.save) {
  10598. this._oCellEditor.save();
  10599. }
  10600. // Backward compatibility
  10601. else if(this._oCellEditor.isActive) {
  10602. var newData = this._oCellEditor.value;
  10603. // Copy the data to pass to the event
  10604. //var oldData = YAHOO.widget.DataTable._cloneObject(this._oCellEditor.record.getData(this._oCellEditor.column.key));
  10605. var oldData = this._oCellEditor.record.getData(this._oCellEditor.column.key);
  10606. // Validate input data
  10607. if(this._oCellEditor.validator) {
  10608. newData = this._oCellEditor.value = this._oCellEditor.validator.call(this, newData, oldData, this._oCellEditor);
  10609. if(newData === null ) {
  10610. this.resetCellEditor();
  10611. this.fireEvent("editorRevertEvent",
  10612. {editor:this._oCellEditor, oldData:oldData, newData:newData});
  10613. YAHOO.log("Could not save Cell Editor input due to invalid data " +
  10614. lang.dump(newData), "warn", this.toString());
  10615. return;
  10616. }
  10617. }
  10618. // Update the Record
  10619. this._oRecordSet.updateRecordValue(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value);
  10620. // Update the UI
  10621. this.formatCell(this._oCellEditor.cell.firstChild);
  10622. // Bug fix 1764044
  10623. this._oChainRender.add({
  10624. method: function() {
  10625. this.validateColumnWidths();
  10626. },
  10627. scope: this
  10628. });
  10629. this._oChainRender.run();
  10630. // Clear out the Cell Editor
  10631. this.resetCellEditor();
  10632. this.fireEvent("editorSaveEvent",
  10633. {editor:this._oCellEditor, oldData:oldData, newData:newData});
  10634. YAHOO.log("Cell Editor input saved", "info", this.toString());
  10635. }
  10636. }
  10637. },
  10638. /**
  10639. * Cancels active CellEditor.
  10640. *
  10641. * @method cancelCellEditor
  10642. */
  10643. cancelCellEditor : function() {
  10644. if(this._oCellEditor) {
  10645. if(this._oCellEditor.cancel) {
  10646. this._oCellEditor.cancel();
  10647. }
  10648. // Backward compatibility
  10649. else if(this._oCellEditor.isActive) {
  10650. this.resetCellEditor();
  10651. //TODO: preserve values for the event?
  10652. this.fireEvent("editorCancelEvent", {editor:this._oCellEditor});
  10653. YAHOO.log("Cell Editor input canceled", "info", this.toString());
  10654. }
  10655. YAHOO.log("CellEditor input canceled", "info", this.toString());
  10656. }
  10657. },
  10658. /**
  10659. * Destroys active CellEditor instance and UI.
  10660. *
  10661. * @method destroyCellEditor
  10662. */
  10663. destroyCellEditor : function() {
  10664. if(this._oCellEditor) {
  10665. this._oCellEditor.destroy();
  10666. this._oCellEditor = null;
  10667. }
  10668. },
  10669. /**
  10670. * Passes through showEvent of the active CellEditor.
  10671. *
  10672. * @method _onEditorShowEvent
  10673. * @param oArgs {Object} Custom Event args.
  10674. * @private
  10675. */
  10676. _onEditorShowEvent : function(oArgs) {
  10677. this.fireEvent("editorShowEvent", oArgs);
  10678. },
  10679. /**
  10680. * Passes through keydownEvent of the active CellEditor.
  10681. * @param oArgs {Object} Custom Event args.
  10682. *
  10683. * @method _onEditorKeydownEvent
  10684. * @private
  10685. */
  10686. _onEditorKeydownEvent : function(oArgs) {
  10687. this.fireEvent("editorKeydownEvent", oArgs);
  10688. },
  10689. /**
  10690. * Passes through revertEvent of the active CellEditor.
  10691. *
  10692. * @method _onEditorRevertEvent
  10693. * @param oArgs {Object} Custom Event args.
  10694. * @private
  10695. */
  10696. _onEditorRevertEvent : function(oArgs) {
  10697. this.fireEvent("editorRevertEvent", oArgs);
  10698. },
  10699. /**
  10700. * Passes through saveEvent of the active CellEditor.
  10701. *
  10702. * @method _onEditorSaveEvent
  10703. * @param oArgs {Object} Custom Event args.
  10704. * @private
  10705. */
  10706. _onEditorSaveEvent : function(oArgs) {
  10707. this.fireEvent("editorSaveEvent", oArgs);
  10708. },
  10709. /**
  10710. * Passes through cancelEvent of the active CellEditor.
  10711. *
  10712. * @method _onEditorCancelEvent
  10713. * @param oArgs {Object} Custom Event args.
  10714. * @private
  10715. */
  10716. _onEditorCancelEvent : function(oArgs) {
  10717. this.fireEvent("editorCancelEvent", oArgs);
  10718. },
  10719. /**
  10720. * Passes through blurEvent of the active CellEditor.
  10721. *
  10722. * @method _onEditorBlurEvent
  10723. * @param oArgs {Object} Custom Event args.
  10724. * @private
  10725. */
  10726. _onEditorBlurEvent : function(oArgs) {
  10727. this.fireEvent("editorBlurEvent", oArgs);
  10728. },
  10729. /**
  10730. * Passes through blockEvent of the active CellEditor.
  10731. *
  10732. * @method _onEditorBlockEvent
  10733. * @param oArgs {Object} Custom Event args.
  10734. * @private
  10735. */
  10736. _onEditorBlockEvent : function(oArgs) {
  10737. this.fireEvent("editorBlockEvent", oArgs);
  10738. },
  10739. /**
  10740. * Passes through unblockEvent of the active CellEditor.
  10741. *
  10742. * @method _onEditorUnblockEvent
  10743. * @param oArgs {Object} Custom Event args.
  10744. * @private
  10745. */
  10746. _onEditorUnblockEvent : function(oArgs) {
  10747. this.fireEvent("editorUnblockEvent", oArgs);
  10748. },
  10749. /**
  10750. * Public handler of the editorBlurEvent. By default, saves on blur if
  10751. * disableBtns is true, otherwise cancels on blur.
  10752. *
  10753. * @method onEditorBlurEvent
  10754. * @param oArgs {Object} Custom Event args.
  10755. */
  10756. onEditorBlurEvent : function(oArgs) {
  10757. if(oArgs.editor.disableBtns) {
  10758. // Save on blur
  10759. if(oArgs.editor.save) { // Backward incompatible
  10760. oArgs.editor.save();
  10761. }
  10762. }
  10763. else if(oArgs.editor.cancel) { // Backward incompatible
  10764. // Cancel on blur
  10765. oArgs.editor.cancel();
  10766. }
  10767. },
  10768. /**
  10769. * Public handler of the editorBlockEvent. By default, disables DataTable UI.
  10770. *
  10771. * @method onEditorBlockEvent
  10772. * @param oArgs {Object} Custom Event args.
  10773. */
  10774. onEditorBlockEvent : function(oArgs) {
  10775. this.disable();
  10776. },
  10777. /**
  10778. * Public handler of the editorUnblockEvent. By default, undisables DataTable UI.
  10779. *
  10780. * @method onEditorUnblockEvent
  10781. * @param oArgs {Object} Custom Event args.
  10782. */
  10783. onEditorUnblockEvent : function(oArgs) {
  10784. this.undisable();
  10785. },
  10786. // ABSTRACT METHODS
  10787. /**
  10788. * Overridable method gives implementers a hook to access data before
  10789. * it gets added to RecordSet and rendered to the TBODY.
  10790. *
  10791. * @method doBeforeLoadData
  10792. * @param sRequest {String} Original request.
  10793. * @param oResponse {Object} Response object.
  10794. * @param oPayload {MIXED} additional arguments
  10795. * @return {Boolean} Return true to continue loading data into RecordSet and
  10796. * updating DataTable with new Records, false to cancel.
  10797. */
  10798. doBeforeLoadData : function(sRequest, oResponse, oPayload) {
  10799. return true;
  10800. },
  10801. /////////////////////////////////////////////////////////////////////////////
  10802. //
  10803. // Public Custom Event Handlers
  10804. //
  10805. /////////////////////////////////////////////////////////////////////////////
  10806. /**
  10807. * Overridable custom event handler to sort Column.
  10808. *
  10809. * @method onEventSortColumn
  10810. * @param oArgs.event {HTMLEvent} Event object.
  10811. * @param oArgs.target {HTMLElement} Target element.
  10812. */
  10813. onEventSortColumn : function(oArgs) {
  10814. //TODO: support form elements in sortable columns
  10815. var evt = oArgs.event;
  10816. var target = oArgs.target;
  10817. var el = this.getThEl(target) || this.getTdEl(target);
  10818. if(el) {
  10819. var oColumn = this.getColumn(el);
  10820. if(oColumn.sortable) {
  10821. Ev.stopEvent(evt);
  10822. this.sortColumn(oColumn);
  10823. }
  10824. }
  10825. else {
  10826. YAHOO.log("Could not find Column for " + target, "warn", this.toString());
  10827. }
  10828. },
  10829. /**
  10830. * Overridable custom event handler to select Column.
  10831. *
  10832. * @method onEventSelectColumn
  10833. * @param oArgs.event {HTMLEvent} Event object.
  10834. * @param oArgs.target {HTMLElement} Target element.
  10835. */
  10836. onEventSelectColumn : function(oArgs) {
  10837. this.selectColumn(oArgs.target);
  10838. },
  10839. /**
  10840. * Overridable custom event handler to highlight Column. Accounts for spurious
  10841. * caused-by-child events.
  10842. *
  10843. * @method onEventHighlightColumn
  10844. * @param oArgs.event {HTMLEvent} Event object.
  10845. * @param oArgs.target {HTMLElement} Target element.
  10846. */
  10847. onEventHighlightColumn : function(oArgs) {
  10848. //TODO: filter for all spurious events at a lower level
  10849. if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
  10850. this.highlightColumn(oArgs.target);
  10851. }
  10852. },
  10853. /**
  10854. * Overridable custom event handler to unhighlight Column. Accounts for spurious
  10855. * caused-by-child events.
  10856. *
  10857. * @method onEventUnhighlightColumn
  10858. * @param oArgs.event {HTMLEvent} Event object.
  10859. * @param oArgs.target {HTMLElement} Target element.
  10860. */
  10861. onEventUnhighlightColumn : function(oArgs) {
  10862. //TODO: filter for all spurious events at a lower level
  10863. if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
  10864. this.unhighlightColumn(oArgs.target);
  10865. }
  10866. },
  10867. /**
  10868. * Overridable custom event handler to manage selection according to desktop paradigm.
  10869. *
  10870. * @method onEventSelectRow
  10871. * @param oArgs.event {HTMLEvent} Event object.
  10872. * @param oArgs.target {HTMLElement} Target element.
  10873. */
  10874. onEventSelectRow : function(oArgs) {
  10875. var sMode = this.get("selectionMode");
  10876. if(sMode == "single") {
  10877. this._handleSingleSelectionByMouse(oArgs);
  10878. }
  10879. else {
  10880. this._handleStandardSelectionByMouse(oArgs);
  10881. }
  10882. },
  10883. /**
  10884. * Overridable custom event handler to select cell.
  10885. *
  10886. * @method onEventSelectCell
  10887. * @param oArgs.event {HTMLEvent} Event object.
  10888. * @param oArgs.target {HTMLElement} Target element.
  10889. */
  10890. onEventSelectCell : function(oArgs) {
  10891. var sMode = this.get("selectionMode");
  10892. if(sMode == "cellblock") {
  10893. this._handleCellBlockSelectionByMouse(oArgs);
  10894. }
  10895. else if(sMode == "cellrange") {
  10896. this._handleCellRangeSelectionByMouse(oArgs);
  10897. }
  10898. else {
  10899. this._handleSingleCellSelectionByMouse(oArgs);
  10900. }
  10901. },
  10902. /**
  10903. * Overridable custom event handler to highlight row. Accounts for spurious
  10904. * caused-by-child events.
  10905. *
  10906. * @method onEventHighlightRow
  10907. * @param oArgs.event {HTMLEvent} Event object.
  10908. * @param oArgs.target {HTMLElement} Target element.
  10909. */
  10910. onEventHighlightRow : function(oArgs) {
  10911. //TODO: filter for all spurious events at a lower level
  10912. if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
  10913. this.highlightRow(oArgs.target);
  10914. }
  10915. },
  10916. /**
  10917. * Overridable custom event handler to unhighlight row. Accounts for spurious
  10918. * caused-by-child events.
  10919. *
  10920. * @method onEventUnhighlightRow
  10921. * @param oArgs.event {HTMLEvent} Event object.
  10922. * @param oArgs.target {HTMLElement} Target element.
  10923. */
  10924. onEventUnhighlightRow : function(oArgs) {
  10925. //TODO: filter for all spurious events at a lower level
  10926. if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
  10927. this.unhighlightRow(oArgs.target);
  10928. }
  10929. },
  10930. /**
  10931. * Overridable custom event handler to highlight cell. Accounts for spurious
  10932. * caused-by-child events.
  10933. *
  10934. * @method onEventHighlightCell
  10935. * @param oArgs.event {HTMLEvent} Event object.
  10936. * @param oArgs.target {HTMLElement} Target element.
  10937. */
  10938. onEventHighlightCell : function(oArgs) {
  10939. //TODO: filter for all spurious events at a lower level
  10940. if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
  10941. this.highlightCell(oArgs.target);
  10942. }
  10943. },
  10944. /**
  10945. * Overridable custom event handler to unhighlight cell. Accounts for spurious
  10946. * caused-by-child events.
  10947. *
  10948. * @method onEventUnhighlightCell
  10949. * @param oArgs.event {HTMLEvent} Event object.
  10950. * @param oArgs.target {HTMLElement} Target element.
  10951. */
  10952. onEventUnhighlightCell : function(oArgs) {
  10953. //TODO: filter for all spurious events at a lower level
  10954. if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
  10955. this.unhighlightCell(oArgs.target);
  10956. }
  10957. },
  10958. /**
  10959. * Overridable custom event handler to format cell.
  10960. *
  10961. * @method onEventFormatCell
  10962. * @param oArgs.event {HTMLEvent} Event object.
  10963. * @param oArgs.target {HTMLElement} Target element.
  10964. */
  10965. onEventFormatCell : function(oArgs) {
  10966. var target = oArgs.target;
  10967. var elCell = this.getTdEl(target);
  10968. if(elCell) {
  10969. var oColumn = this.getColumn(elCell.cellIndex);
  10970. this.formatCell(elCell.firstChild, this.getRecord(elCell), oColumn);
  10971. }
  10972. else {
  10973. YAHOO.log("Could not format cell " + target, "warn", this.toString());
  10974. }
  10975. },
  10976. /**
  10977. * Overridable custom event handler to edit cell.
  10978. *
  10979. * @method onEventShowCellEditor
  10980. * @param oArgs.event {HTMLEvent} Event object.
  10981. * @param oArgs.target {HTMLElement} Target element.
  10982. */
  10983. onEventShowCellEditor : function(oArgs) {
  10984. this.showCellEditor(oArgs.target);
  10985. },
  10986. /**
  10987. * Overridable custom event handler to save active CellEditor input.
  10988. *
  10989. * @method onEventSaveCellEditor
  10990. */
  10991. onEventSaveCellEditor : function(oArgs) {
  10992. if(this._oCellEditor) {
  10993. if(this._oCellEditor.save) {
  10994. this._oCellEditor.save();
  10995. }
  10996. // Backward compatibility
  10997. else {
  10998. this.saveCellEditor();
  10999. }
  11000. }
  11001. },
  11002. /**
  11003. * Overridable custom event handler to cancel active CellEditor.
  11004. *
  11005. * @method onEventCancelCellEditor
  11006. */
  11007. onEventCancelCellEditor : function(oArgs) {
  11008. if(this._oCellEditor) {
  11009. if(this._oCellEditor.cancel) {
  11010. this._oCellEditor.cancel();
  11011. }
  11012. // Backward compatibility
  11013. else {
  11014. this.cancelCellEditor();
  11015. }
  11016. }
  11017. },
  11018. /**
  11019. * Callback function receives data from DataSource and populates an entire
  11020. * DataTable with Records and TR elements, clearing previous Records, if any.
  11021. *
  11022. * @method onDataReturnInitializeTable
  11023. * @param sRequest {String} Original request.
  11024. * @param oResponse {Object} Response object.
  11025. * @param oPayload {MIXED} (optional) Additional argument(s)
  11026. */
  11027. onDataReturnInitializeTable : function(sRequest, oResponse, oPayload) {
  11028. if((this instanceof DT) && this._sId) {
  11029. this.initializeTable();
  11030. this.onDataReturnSetRows(sRequest,oResponse,oPayload);
  11031. }
  11032. },
  11033. /**
  11034. * Callback function receives reponse from DataSource, replaces all existing
  11035. * Records in RecordSet, updates TR elements with new data, and updates state
  11036. * UI for pagination and sorting from payload data, if necessary.
  11037. *
  11038. * @method onDataReturnReplaceRows
  11039. * @param oRequest {MIXED} Original generated request.
  11040. * @param oResponse {Object} Response object.
  11041. * @param oPayload {MIXED} (optional) Additional argument(s)
  11042. */
  11043. onDataReturnReplaceRows : function(oRequest, oResponse, oPayload) {
  11044. if((this instanceof DT) && this._sId) {
  11045. this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
  11046. // Pass data through abstract method for any transformations
  11047. var ok = this.doBeforeLoadData(oRequest, oResponse, oPayload),
  11048. pag = this.get('paginator'),
  11049. index = 0;
  11050. // Data ok to set
  11051. if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
  11052. // Update Records
  11053. this._oRecordSet.reset();
  11054. if (this.get('dynamicData')) {
  11055. if (oPayload && oPayload.pagination &&
  11056. lang.isNumber(oPayload.pagination.recordOffset)) {
  11057. index = oPayload.pagination.recordOffset;
  11058. } else if (pag) {
  11059. index = pag.getStartIndex();
  11060. }
  11061. }
  11062. this._oRecordSet.setRecords(oResponse.results, index | 0);
  11063. // Update state
  11064. this._handleDataReturnPayload(oRequest, oResponse, oPayload);
  11065. // Update UI
  11066. this.render();
  11067. }
  11068. // Error
  11069. else if(ok && oResponse.error) {
  11070. this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
  11071. }
  11072. }
  11073. },
  11074. /**
  11075. * Callback function receives data from DataSource and appends to an existing
  11076. * DataTable new Records and, if applicable, creates or updates
  11077. * corresponding TR elements.
  11078. *
  11079. * @method onDataReturnAppendRows
  11080. * @param sRequest {String} Original request.
  11081. * @param oResponse {Object} Response object.
  11082. * @param oPayload {MIXED} (optional) Additional argument(s)
  11083. */
  11084. onDataReturnAppendRows : function(sRequest, oResponse, oPayload) {
  11085. if((this instanceof DT) && this._sId) {
  11086. this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
  11087. // Pass data through abstract method for any transformations
  11088. var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
  11089. // Data ok to append
  11090. if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
  11091. // Append rows
  11092. this.addRows(oResponse.results);
  11093. // Update state
  11094. this._handleDataReturnPayload(sRequest, oResponse, oPayload);
  11095. }
  11096. // Error
  11097. else if(ok && oResponse.error) {
  11098. this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
  11099. }
  11100. }
  11101. },
  11102. /**
  11103. * Callback function receives data from DataSource and inserts new records
  11104. * starting at the index specified in oPayload.insertIndex. The value for
  11105. * oPayload.insertIndex can be populated when sending the request to the DataSource,
  11106. * or by accessing oPayload.insertIndex with the doBeforeLoadData() method at runtime.
  11107. * If applicable, creates or updates corresponding TR elements.
  11108. *
  11109. * @method onDataReturnInsertRows
  11110. * @param sRequest {String} Original request.
  11111. * @param oResponse {Object} Response object.
  11112. * @param oPayload {MIXED} Argument payload, looks in oPayload.insertIndex.
  11113. */
  11114. onDataReturnInsertRows : function(sRequest, oResponse, oPayload) {
  11115. if((this instanceof DT) && this._sId) {
  11116. this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
  11117. // Pass data through abstract method for any transformations
  11118. var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
  11119. // Data ok to append
  11120. if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
  11121. // Insert rows
  11122. this.addRows(oResponse.results, (oPayload ? oPayload.insertIndex : 0));
  11123. // Update state
  11124. this._handleDataReturnPayload(sRequest, oResponse, oPayload);
  11125. }
  11126. // Error
  11127. else if(ok && oResponse.error) {
  11128. this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
  11129. }
  11130. }
  11131. },
  11132. /**
  11133. * Callback function receives data from DataSource and incrementally updates Records
  11134. * starting at the index specified in oPayload.updateIndex. The value for
  11135. * oPayload.updateIndex can be populated when sending the request to the DataSource,
  11136. * or by accessing oPayload.updateIndex with the doBeforeLoadData() method at runtime.
  11137. * If applicable, creates or updates corresponding TR elements.
  11138. *
  11139. * @method onDataReturnUpdateRows
  11140. * @param sRequest {String} Original request.
  11141. * @param oResponse {Object} Response object.
  11142. * @param oPayload {MIXED} Argument payload, looks in oPayload.updateIndex.
  11143. */
  11144. onDataReturnUpdateRows : function(sRequest, oResponse, oPayload) {
  11145. if((this instanceof DT) && this._sId) {
  11146. this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
  11147. // Pass data through abstract method for any transformations
  11148. var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
  11149. // Data ok to append
  11150. if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
  11151. // Insert rows
  11152. this.updateRows((oPayload ? oPayload.updateIndex : 0), oResponse.results);
  11153. // Update state
  11154. this._handleDataReturnPayload(sRequest, oResponse, oPayload);
  11155. }
  11156. // Error
  11157. else if(ok && oResponse.error) {
  11158. this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
  11159. }
  11160. }
  11161. },
  11162. /**
  11163. * Callback function receives reponse from DataSource and populates the
  11164. * RecordSet with the results.
  11165. *
  11166. * @method onDataReturnSetRows
  11167. * @param oRequest {MIXED} Original generated request.
  11168. * @param oResponse {Object} Response object.
  11169. * @param oPayload {MIXED} (optional) Additional argument(s)
  11170. */
  11171. onDataReturnSetRows : function(oRequest, oResponse, oPayload) {
  11172. if((this instanceof DT) && this._sId) {
  11173. this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
  11174. // Pass data through abstract method for any transformations
  11175. var ok = this.doBeforeLoadData(oRequest, oResponse, oPayload),
  11176. pag = this.get('paginator'),
  11177. index = 0;
  11178. // Data ok to set
  11179. if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
  11180. // Update Records
  11181. if (this.get('dynamicData')) {
  11182. if (oPayload && oPayload.pagination &&
  11183. lang.isNumber(oPayload.pagination.recordOffset)) {
  11184. index = oPayload.pagination.recordOffset;
  11185. } else if (pag) {
  11186. index = pag.getStartIndex();
  11187. }
  11188. this._oRecordSet.reset(); // Bug 2290604: dyanmic data shouldn't keep accumulating by default
  11189. }
  11190. this._oRecordSet.setRecords(oResponse.results, index | 0);
  11191. // Update state
  11192. this._handleDataReturnPayload(oRequest, oResponse, oPayload);
  11193. // Update UI
  11194. this.render();
  11195. }
  11196. // Error
  11197. else if(ok && oResponse.error) {
  11198. this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
  11199. }
  11200. }
  11201. else {
  11202. YAHOO.log("Instance destroyed before data returned.","info",this.toString());
  11203. }
  11204. },
  11205. /**
  11206. * Hook to update oPayload before consumption.
  11207. *
  11208. * @method handleDataReturnPayload
  11209. * @param oRequest {MIXED} Original generated request.
  11210. * @param oResponse {Object} Response object.
  11211. * @param oPayload {MIXED} State values.
  11212. * @return oPayload {MIXED} State values.
  11213. */
  11214. handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
  11215. return oPayload;
  11216. },
  11217. /**
  11218. * Updates the DataTable with state data sent in an onDataReturn* payload.
  11219. *
  11220. * @method handleDataReturnPayload
  11221. * @param oRequest {MIXED} Original generated request.
  11222. * @param oResponse {Object} Response object.
  11223. * @param oPayload {MIXED} State values
  11224. */
  11225. _handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
  11226. oPayload = this.handleDataReturnPayload(oRequest, oResponse, oPayload);
  11227. if(oPayload) {
  11228. // Update pagination
  11229. var oPaginator = this.get('paginator');
  11230. if (oPaginator) {
  11231. // Update totalRecords
  11232. if(this.get("dynamicData")) {
  11233. if (widget.Paginator.isNumeric(oPayload.totalRecords)) {
  11234. oPaginator.set('totalRecords',oPayload.totalRecords);
  11235. }
  11236. }
  11237. else {
  11238. oPaginator.set('totalRecords',this._oRecordSet.getLength());
  11239. }
  11240. // Update other paginator values
  11241. if (lang.isObject(oPayload.pagination)) {
  11242. oPaginator.set('rowsPerPage',oPayload.pagination.rowsPerPage);
  11243. oPaginator.set('recordOffset',oPayload.pagination.recordOffset);
  11244. }
  11245. }
  11246. // Update sorting
  11247. if (oPayload.sortedBy) {
  11248. // Set the sorting values in preparation for refresh
  11249. this.set('sortedBy', oPayload.sortedBy);
  11250. }
  11251. // Backwards compatibility for sorting
  11252. else if (oPayload.sorting) {
  11253. // Set the sorting values in preparation for refresh
  11254. this.set('sortedBy', oPayload.sorting);
  11255. }
  11256. }
  11257. },
  11258. /////////////////////////////////////////////////////////////////////////////
  11259. //
  11260. // Custom Events
  11261. //
  11262. /////////////////////////////////////////////////////////////////////////////
  11263. /**
  11264. * Fired when the DataTable's rows are rendered from an initialized state.
  11265. *
  11266. * @event initEvent
  11267. */
  11268. /**
  11269. * Fired before the DataTable's DOM is rendered or modified.
  11270. *
  11271. * @event beforeRenderEvent
  11272. */
  11273. /**
  11274. * Fired when the DataTable's DOM is rendered or modified.
  11275. *
  11276. * @event renderEvent
  11277. */
  11278. /**
  11279. * Fired when the DataTable's post-render routine is complete, including
  11280. * Column width validations.
  11281. *
  11282. * @event postRenderEvent
  11283. */
  11284. /**
  11285. * Fired when the DataTable is disabled.
  11286. *
  11287. * @event disableEvent
  11288. */
  11289. /**
  11290. * Fired when the DataTable is undisabled.
  11291. *
  11292. * @event undisableEvent
  11293. */
  11294. /**
  11295. * Fired when data is returned from DataSource but before it is consumed by
  11296. * DataTable.
  11297. *
  11298. * @event dataReturnEvent
  11299. * @param oArgs.request {String} Original request.
  11300. * @param oArgs.response {Object} Response object.
  11301. */
  11302. /**
  11303. * Fired when the DataTable has a focus event.
  11304. *
  11305. * @event tableFocusEvent
  11306. */
  11307. /**
  11308. * Fired when the DataTable THEAD element has a focus event.
  11309. *
  11310. * @event theadFocusEvent
  11311. */
  11312. /**
  11313. * Fired when the DataTable TBODY element has a focus event.
  11314. *
  11315. * @event tbodyFocusEvent
  11316. */
  11317. /**
  11318. * Fired when the DataTable has a blur event.
  11319. *
  11320. * @event tableBlurEvent
  11321. */
  11322. /*TODO implement theadBlurEvent
  11323. * Fired when the DataTable THEAD element has a blur event.
  11324. *
  11325. * @event theadBlurEvent
  11326. */
  11327. /*TODO: implement tbodyBlurEvent
  11328. * Fired when the DataTable TBODY element has a blur event.
  11329. *
  11330. * @event tbodyBlurEvent
  11331. */
  11332. /**
  11333. * Fired when the DataTable has a key event.
  11334. *
  11335. * @event tableKeyEvent
  11336. * @param oArgs.event {HTMLEvent} The event object.
  11337. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11338. */
  11339. /**
  11340. * Fired when the DataTable THEAD element has a key event.
  11341. *
  11342. * @event theadKeyEvent
  11343. * @param oArgs.event {HTMLEvent} The event object.
  11344. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11345. */
  11346. /**
  11347. * Fired when the DataTable TBODY element has a key event.
  11348. *
  11349. * @event tbodyKeyEvent
  11350. * @param oArgs.event {HTMLEvent} The event object.
  11351. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11352. */
  11353. /**
  11354. * Fired when the DataTable has a mouseover.
  11355. *
  11356. * @event tableMouseoverEvent
  11357. * @param oArgs.event {HTMLEvent} The event object.
  11358. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11359. *
  11360. */
  11361. /**
  11362. * Fired when the DataTable has a mouseout.
  11363. *
  11364. * @event tableMouseoutEvent
  11365. * @param oArgs.event {HTMLEvent} The event object.
  11366. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11367. *
  11368. */
  11369. /**
  11370. * Fired when the DataTable has a mousedown.
  11371. *
  11372. * @event tableMousedownEvent
  11373. * @param oArgs.event {HTMLEvent} The event object.
  11374. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11375. *
  11376. */
  11377. /**
  11378. * Fired when the DataTable has a mouseup.
  11379. *
  11380. * @event tableMouseupEvent
  11381. * @param oArgs.event {HTMLEvent} The event object.
  11382. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11383. *
  11384. */
  11385. /**
  11386. * Fired when the DataTable has a click.
  11387. *
  11388. * @event tableClickEvent
  11389. * @param oArgs.event {HTMLEvent} The event object.
  11390. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11391. *
  11392. */
  11393. /**
  11394. * Fired when the DataTable has a dblclick.
  11395. *
  11396. * @event tableDblclickEvent
  11397. * @param oArgs.event {HTMLEvent} The event object.
  11398. * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
  11399. *
  11400. */
  11401. /**
  11402. * Fired when a message is shown in the DataTable's message element.
  11403. *
  11404. * @event tableMsgShowEvent
  11405. * @param oArgs.html {String} The HTML displayed.
  11406. * @param oArgs.className {String} The className assigned.
  11407. *
  11408. */
  11409. /**
  11410. * Fired when the DataTable's message element is hidden.
  11411. *
  11412. * @event tableMsgHideEvent
  11413. */
  11414. /**
  11415. * Fired when a THEAD row has a mouseover.
  11416. *
  11417. * @event theadRowMouseoverEvent
  11418. * @param oArgs.event {HTMLEvent} The event object.
  11419. * @param oArgs.target {HTMLElement} The TR element.
  11420. */
  11421. /**
  11422. * Fired when a THEAD row has a mouseout.
  11423. *
  11424. * @event theadRowMouseoutEvent
  11425. * @param oArgs.event {HTMLEvent} The event object.
  11426. * @param oArgs.target {HTMLElement} The TR element.
  11427. */
  11428. /**
  11429. * Fired when a THEAD row has a mousedown.
  11430. *
  11431. * @event theadRowMousedownEvent
  11432. * @param oArgs.event {HTMLEvent} The event object.
  11433. * @param oArgs.target {HTMLElement} The TR element.
  11434. */
  11435. /**
  11436. * Fired when a THEAD row has a mouseup.
  11437. *
  11438. * @event theadRowMouseupEvent
  11439. * @param oArgs.event {HTMLEvent} The event object.
  11440. * @param oArgs.target {HTMLElement} The TR element.
  11441. */
  11442. /**
  11443. * Fired when a THEAD row has a click.
  11444. *
  11445. * @event theadRowClickEvent
  11446. * @param oArgs.event {HTMLEvent} The event object.
  11447. * @param oArgs.target {HTMLElement} The TR element.
  11448. */
  11449. /**
  11450. * Fired when a THEAD row has a dblclick.
  11451. *
  11452. * @event theadRowDblclickEvent
  11453. * @param oArgs.event {HTMLEvent} The event object.
  11454. * @param oArgs.target {HTMLElement} The TR element.
  11455. */
  11456. /**
  11457. * Fired when a THEAD cell has a mouseover.
  11458. *
  11459. * @event theadCellMouseoverEvent
  11460. * @param oArgs.event {HTMLEvent} The event object.
  11461. * @param oArgs.target {HTMLElement} The TH element.
  11462. *
  11463. */
  11464. /**
  11465. * Fired when a THEAD cell has a mouseout.
  11466. *
  11467. * @event theadCellMouseoutEvent
  11468. * @param oArgs.event {HTMLEvent} The event object.
  11469. * @param oArgs.target {HTMLElement} The TH element.
  11470. *
  11471. */
  11472. /**
  11473. * Fired when a THEAD cell has a mousedown.
  11474. *
  11475. * @event theadCellMousedownEvent
  11476. * @param oArgs.event {HTMLEvent} The event object.
  11477. * @param oArgs.target {HTMLElement} The TH element.
  11478. */
  11479. /**
  11480. * Fired when a THEAD cell has a mouseup.
  11481. *
  11482. * @event theadCellMouseupEvent
  11483. * @param oArgs.event {HTMLEvent} The event object.
  11484. * @param oArgs.target {HTMLElement} The TH element.
  11485. */
  11486. /**
  11487. * Fired when a THEAD cell has a click.
  11488. *
  11489. * @event theadCellClickEvent
  11490. * @param oArgs.event {HTMLEvent} The event object.
  11491. * @param oArgs.target {HTMLElement} The TH element.
  11492. */
  11493. /**
  11494. * Fired when a THEAD cell has a dblclick.
  11495. *
  11496. * @event theadCellDblclickEvent
  11497. * @param oArgs.event {HTMLEvent} The event object.
  11498. * @param oArgs.target {HTMLElement} The TH element.
  11499. */
  11500. /**
  11501. * Fired when a THEAD label has a mouseover.
  11502. *
  11503. * @event theadLabelMouseoverEvent
  11504. * @param oArgs.event {HTMLEvent} The event object.
  11505. * @param oArgs.target {HTMLElement} The SPAN element.
  11506. *
  11507. */
  11508. /**
  11509. * Fired when a THEAD label has a mouseout.
  11510. *
  11511. * @event theadLabelMouseoutEvent
  11512. * @param oArgs.event {HTMLEvent} The event object.
  11513. * @param oArgs.target {HTMLElement} The SPAN element.
  11514. *
  11515. */
  11516. /**
  11517. * Fired when a THEAD label has a mousedown.
  11518. *
  11519. * @event theadLabelMousedownEvent
  11520. * @param oArgs.event {HTMLEvent} The event object.
  11521. * @param oArgs.target {HTMLElement} The SPAN element.
  11522. */
  11523. /**
  11524. * Fired when a THEAD label has a mouseup.
  11525. *
  11526. * @event theadLabelMouseupEvent
  11527. * @param oArgs.event {HTMLEvent} The event object.
  11528. * @param oArgs.target {HTMLElement} The SPAN element.
  11529. */
  11530. /**
  11531. * Fired when a THEAD label has a click.
  11532. *
  11533. * @event theadLabelClickEvent
  11534. * @param oArgs.event {HTMLEvent} The event object.
  11535. * @param oArgs.target {HTMLElement} The SPAN element.
  11536. */
  11537. /**
  11538. * Fired when a THEAD label has a dblclick.
  11539. *
  11540. * @event theadLabelDblclickEvent
  11541. * @param oArgs.event {HTMLEvent} The event object.
  11542. * @param oArgs.target {HTMLElement} The SPAN element.
  11543. */
  11544. /**
  11545. * Fired when a column is sorted.
  11546. *
  11547. * @event columnSortEvent
  11548. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11549. * @param oArgs.dir {String} Sort direction: YAHOO.widget.DataTable.CLASS_ASC
  11550. * or YAHOO.widget.DataTable.CLASS_DESC.
  11551. */
  11552. /**
  11553. * Fired when a column width is set.
  11554. *
  11555. * @event columnSetWidthEvent
  11556. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11557. * @param oArgs.width {Number} The width in pixels.
  11558. */
  11559. /**
  11560. * Fired when a column width is unset.
  11561. *
  11562. * @event columnUnsetWidthEvent
  11563. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11564. */
  11565. /**
  11566. * Fired when a column is drag-resized.
  11567. *
  11568. * @event columnResizeEvent
  11569. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11570. * @param oArgs.target {HTMLElement} The TH element.
  11571. * @param oArgs.width {Number} Width in pixels.
  11572. */
  11573. /**
  11574. * Fired when a Column is moved to a new index.
  11575. *
  11576. * @event columnReorderEvent
  11577. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11578. * @param oArgs.oldIndex {Number} The previous index position.
  11579. */
  11580. /**
  11581. * Fired when a column is hidden.
  11582. *
  11583. * @event columnHideEvent
  11584. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11585. */
  11586. /**
  11587. * Fired when a column is shown.
  11588. *
  11589. * @event columnShowEvent
  11590. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11591. */
  11592. /**
  11593. * Fired when a column is selected.
  11594. *
  11595. * @event columnSelectEvent
  11596. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11597. */
  11598. /**
  11599. * Fired when a column is unselected.
  11600. *
  11601. * @event columnUnselectEvent
  11602. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11603. */
  11604. /**
  11605. * Fired when a column is removed.
  11606. *
  11607. * @event columnRemoveEvent
  11608. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11609. */
  11610. /**
  11611. * Fired when a column is inserted.
  11612. *
  11613. * @event columnInsertEvent
  11614. * @param oArgs.column {YAHOO.widget.Column} The Column instance.
  11615. * @param oArgs.index {Number} The index position.
  11616. */
  11617. /**
  11618. * Fired when a column is highlighted.
  11619. *
  11620. * @event columnHighlightEvent
  11621. * @param oArgs.column {YAHOO.widget.Column} The highlighted Column.
  11622. */
  11623. /**
  11624. * Fired when a column is unhighlighted.
  11625. *
  11626. * @event columnUnhighlightEvent
  11627. * @param oArgs.column {YAHOO.widget.Column} The unhighlighted Column.
  11628. */
  11629. /**
  11630. * Fired when a row has a mouseover.
  11631. *
  11632. * @event rowMouseoverEvent
  11633. * @param oArgs.event {HTMLEvent} The event object.
  11634. * @param oArgs.target {HTMLElement} The TR element.
  11635. */
  11636. /**
  11637. * Fired when a row has a mouseout.
  11638. *
  11639. * @event rowMouseoutEvent
  11640. * @param oArgs.event {HTMLEvent} The event object.
  11641. * @param oArgs.target {HTMLElement} The TR element.
  11642. */
  11643. /**
  11644. * Fired when a row has a mousedown.
  11645. *
  11646. * @event rowMousedownEvent
  11647. * @param oArgs.event {HTMLEvent} The event object.
  11648. * @param oArgs.target {HTMLElement} The TR element.
  11649. */
  11650. /**
  11651. * Fired when a row has a mouseup.
  11652. *
  11653. * @event rowMouseupEvent
  11654. * @param oArgs.event {HTMLEvent} The event object.
  11655. * @param oArgs.target {HTMLElement} The TR element.
  11656. */
  11657. /**
  11658. * Fired when a row has a click.
  11659. *
  11660. * @event rowClickEvent
  11661. * @param oArgs.event {HTMLEvent} The event object.
  11662. * @param oArgs.target {HTMLElement} The TR element.
  11663. */
  11664. /**
  11665. * Fired when a row has a dblclick.
  11666. *
  11667. * @event rowDblclickEvent
  11668. * @param oArgs.event {HTMLEvent} The event object.
  11669. * @param oArgs.target {HTMLElement} The TR element.
  11670. */
  11671. /**
  11672. * Fired when a row is added.
  11673. *
  11674. * @event rowAddEvent
  11675. * @param oArgs.record {YAHOO.widget.Record} The added Record.
  11676. */
  11677. /**
  11678. * Fired when rows are added.
  11679. *
  11680. * @event rowsAddEvent
  11681. * @param oArgs.record {YAHOO.widget.Record[]} The added Records.
  11682. */
  11683. /**
  11684. * Fired when a row is updated.
  11685. *
  11686. * @event rowUpdateEvent
  11687. * @param oArgs.record {YAHOO.widget.Record} The updated Record.
  11688. * @param oArgs.oldData {Object} Object literal of the old data.
  11689. */
  11690. /**
  11691. * Fired when a row is deleted.
  11692. *
  11693. * @event rowDeleteEvent
  11694. * @param oArgs.oldData {Object} Object literal of the deleted data.
  11695. * @param oArgs.recordIndex {Number} Index of the deleted Record.
  11696. * @param oArgs.trElIndex {Number} Index of the deleted TR element, if on current page.
  11697. */
  11698. /**
  11699. * Fired when rows are deleted.
  11700. *
  11701. * @event rowsDeleteEvent
  11702. * @param oArgs.oldData {Object[]} Array of object literals of the deleted data.
  11703. * @param oArgs.recordIndex {Number} Index of the first deleted Record.
  11704. * @param oArgs.count {Number} Number of deleted Records.
  11705. */
  11706. /**
  11707. * Fired when a row is selected.
  11708. *
  11709. * @event rowSelectEvent
  11710. * @param oArgs.el {HTMLElement} The selected TR element, if applicable.
  11711. * @param oArgs.record {YAHOO.widget.Record} The selected Record.
  11712. */
  11713. /**
  11714. * Fired when a row is unselected.
  11715. *
  11716. * @event rowUnselectEvent
  11717. * @param oArgs.el {HTMLElement} The unselected TR element, if applicable.
  11718. * @param oArgs.record {YAHOO.widget.Record} The unselected Record.
  11719. */
  11720. /**
  11721. * Fired when all row selections are cleared.
  11722. *
  11723. * @event unselectAllRowsEvent
  11724. */
  11725. /**
  11726. * Fired when a row is highlighted.
  11727. *
  11728. * @event rowHighlightEvent
  11729. * @param oArgs.el {HTMLElement} The highlighted TR element.
  11730. * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
  11731. */
  11732. /**
  11733. * Fired when a row is unhighlighted.
  11734. *
  11735. * @event rowUnhighlightEvent
  11736. * @param oArgs.el {HTMLElement} The highlighted TR element.
  11737. * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
  11738. */
  11739. /**
  11740. * Fired when a cell is updated.
  11741. *
  11742. * @event cellUpdateEvent
  11743. * @param oArgs.record {YAHOO.widget.Record} The updated Record.
  11744. * @param oArgs.column {YAHOO.widget.Column} The updated Column.
  11745. * @param oArgs.oldData {Object} Original data value of the updated cell.
  11746. */
  11747. /**
  11748. * Fired when a cell has a mouseover.
  11749. *
  11750. * @event cellMouseoverEvent
  11751. * @param oArgs.event {HTMLEvent} The event object.
  11752. * @param oArgs.target {HTMLElement} The TD element.
  11753. */
  11754. /**
  11755. * Fired when a cell has a mouseout.
  11756. *
  11757. * @event cellMouseoutEvent
  11758. * @param oArgs.event {HTMLEvent} The event object.
  11759. * @param oArgs.target {HTMLElement} The TD element.
  11760. */
  11761. /**
  11762. * Fired when a cell has a mousedown.
  11763. *
  11764. * @event cellMousedownEvent
  11765. * @param oArgs.event {HTMLEvent} The event object.
  11766. * @param oArgs.target {HTMLElement} The TD element.
  11767. */
  11768. /**
  11769. * Fired when a cell has a mouseup.
  11770. *
  11771. * @event cellMouseupEvent
  11772. * @param oArgs.event {HTMLEvent} The event object.
  11773. * @param oArgs.target {HTMLElement} The TD element.
  11774. */
  11775. /**
  11776. * Fired when a cell has a click.
  11777. *
  11778. * @event cellClickEvent
  11779. * @param oArgs.event {HTMLEvent} The event object.
  11780. * @param oArgs.target {HTMLElement} The TD element.
  11781. */
  11782. /**
  11783. * Fired when a cell has a dblclick.
  11784. *
  11785. * @event cellDblclickEvent
  11786. * @param oArgs.event {HTMLEvent} The event object.
  11787. * @param oArgs.target {HTMLElement} The TD element.
  11788. */
  11789. /**
  11790. * Fired when a cell is formatted.
  11791. *
  11792. * @event cellFormatEvent
  11793. * @param oArgs.el {HTMLElement} The formatted TD element.
  11794. * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
  11795. * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
  11796. * @param oArgs.key {String} (deprecated) The key of the formatted cell.
  11797. */
  11798. /**
  11799. * Fired when a cell is selected.
  11800. *
  11801. * @event cellSelectEvent
  11802. * @param oArgs.el {HTMLElement} The selected TD element.
  11803. * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
  11804. * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
  11805. * @param oArgs.key {String} (deprecated) The key of the selected cell.
  11806. */
  11807. /**
  11808. * Fired when a cell is unselected.
  11809. *
  11810. * @event cellUnselectEvent
  11811. * @param oArgs.el {HTMLElement} The unselected TD element.
  11812. * @param oArgs.record {YAHOO.widget.Record} The associated Record.
  11813. * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
  11814. * @param oArgs.key {String} (deprecated) The key of the unselected cell.
  11815. */
  11816. /**
  11817. * Fired when a cell is highlighted.
  11818. *
  11819. * @event cellHighlightEvent
  11820. * @param oArgs.el {HTMLElement} The highlighted TD element.
  11821. * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
  11822. * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
  11823. * @param oArgs.key {String} (deprecated) The key of the highlighted cell.
  11824. */
  11825. /**
  11826. * Fired when a cell is unhighlighted.
  11827. *
  11828. * @event cellUnhighlightEvent
  11829. * @param oArgs.el {HTMLElement} The unhighlighted TD element.
  11830. * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
  11831. * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
  11832. * @param oArgs.key {String} (deprecated) The key of the unhighlighted cell.
  11833. */
  11834. /**
  11835. * Fired when all cell selections are cleared.
  11836. *
  11837. * @event unselectAllCellsEvent
  11838. */
  11839. /**
  11840. * Fired when a CellEditor is shown.
  11841. *
  11842. * @event editorShowEvent
  11843. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  11844. */
  11845. /**
  11846. * Fired when a CellEditor has a keydown.
  11847. *
  11848. * @event editorKeydownEvent
  11849. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  11850. * @param oArgs.event {HTMLEvent} The event object.
  11851. */
  11852. /**
  11853. * Fired when a CellEditor input is reverted.
  11854. *
  11855. * @event editorRevertEvent
  11856. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  11857. * @param oArgs.newData {Object} New data value from form input field.
  11858. * @param oArgs.oldData {Object} Old data value.
  11859. */
  11860. /**
  11861. * Fired when a CellEditor input is saved.
  11862. *
  11863. * @event editorSaveEvent
  11864. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  11865. * @param oArgs.newData {Object} New data value from form input field.
  11866. * @param oArgs.oldData {Object} Old data value.
  11867. */
  11868. /**
  11869. * Fired when a CellEditor input is canceled.
  11870. *
  11871. * @event editorCancelEvent
  11872. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  11873. */
  11874. /**
  11875. * Fired when a CellEditor has a blur event.
  11876. *
  11877. * @event editorBlurEvent
  11878. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  11879. */
  11880. /**
  11881. * Fired when a CellEditor is blocked.
  11882. *
  11883. * @event editorBlockEvent
  11884. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  11885. */
  11886. /**
  11887. * Fired when a CellEditor is unblocked.
  11888. *
  11889. * @event editorUnblockEvent
  11890. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  11891. */
  11892. /**
  11893. * Fired when a link is clicked.
  11894. *
  11895. * @event linkClickEvent
  11896. * @param oArgs.event {HTMLEvent} The event object.
  11897. * @param oArgs.target {HTMLElement} The A element.
  11898. */
  11899. /**
  11900. * Fired when a BUTTON element or INPUT element of type "button", "image",
  11901. * "submit", "reset" is clicked.
  11902. *
  11903. * @event buttonClickEvent
  11904. * @param oArgs.event {HTMLEvent} The event object.
  11905. * @param oArgs.target {HTMLElement} The BUTTON element.
  11906. */
  11907. /**
  11908. * Fired when a CHECKBOX element is clicked.
  11909. *
  11910. * @event checkboxClickEvent
  11911. * @param oArgs.event {HTMLEvent} The event object.
  11912. * @param oArgs.target {HTMLElement} The CHECKBOX element.
  11913. */
  11914. /**
  11915. * Fired when a SELECT element is changed.
  11916. *
  11917. * @event dropdownChangeEvent
  11918. * @param oArgs.event {HTMLEvent} The event object.
  11919. * @param oArgs.target {HTMLElement} The SELECT element.
  11920. */
  11921. /**
  11922. * Fired when a RADIO element is clicked.
  11923. *
  11924. * @event radioClickEvent
  11925. * @param oArgs.event {HTMLEvent} The event object.
  11926. * @param oArgs.target {HTMLElement} The RADIO element.
  11927. */
  11928. /////////////////////////////////////////////////////////////////////////////
  11929. //
  11930. // Deprecated APIs
  11931. //
  11932. /////////////////////////////////////////////////////////////////////////////
  11933. /*
  11934. * @method showCellEditorBtns
  11935. * @deprecated Use CellEditor.renderBtns()
  11936. */
  11937. showCellEditorBtns : function(elContainer) {
  11938. // Buttons
  11939. var elBtnsDiv = elContainer.appendChild(document.createElement("div"));
  11940. Dom.addClass(elBtnsDiv, DT.CLASS_BUTTON);
  11941. // Save button
  11942. var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
  11943. Dom.addClass(elSaveBtn, DT.CLASS_DEFAULT);
  11944. elSaveBtn.innerHTML = "OK";
  11945. Ev.addListener(elSaveBtn, "click", function(oArgs, oSelf) {
  11946. oSelf.onEventSaveCellEditor(oArgs, oSelf);
  11947. oSelf.focusTbodyEl();
  11948. }, this, true);
  11949. // Cancel button
  11950. var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
  11951. elCancelBtn.innerHTML = "Cancel";
  11952. Ev.addListener(elCancelBtn, "click", function(oArgs, oSelf) {
  11953. oSelf.onEventCancelCellEditor(oArgs, oSelf);
  11954. oSelf.focusTbodyEl();
  11955. }, this, true);
  11956. YAHOO.log("The method showCellEditorBtns() has been deprecated." +
  11957. " Please use the CellEditor class.", "warn", this.toString());
  11958. },
  11959. /**
  11960. * @method resetCellEditor
  11961. * @deprecated Use destroyCellEditor
  11962. */
  11963. resetCellEditor : function() {
  11964. var elContainer = this._oCellEditor.container;
  11965. elContainer.style.display = "none";
  11966. Ev.purgeElement(elContainer, true);
  11967. elContainer.innerHTML = "";
  11968. this._oCellEditor.value = null;
  11969. this._oCellEditor.isActive = false;
  11970. YAHOO.log("The method resetCellEditor() has been deprecated." +
  11971. " Please use the CellEditor class.", "warn", this.toString());
  11972. },
  11973. /**
  11974. * @event editorUpdateEvent
  11975. * @deprecated Use CellEditor class.
  11976. */
  11977. /**
  11978. * @method getBody
  11979. * @deprecated Use getTbodyEl().
  11980. */
  11981. getBody : function() {
  11982. // Backward compatibility
  11983. YAHOO.log("The method getBody() has been deprecated" +
  11984. " in favor of getTbodyEl()", "warn", this.toString());
  11985. return this.getTbodyEl();
  11986. },
  11987. /**
  11988. * @method getCell
  11989. * @deprecated Use getTdEl().
  11990. */
  11991. getCell : function(index) {
  11992. // Backward compatibility
  11993. YAHOO.log("The method getCell() has been deprecated" +
  11994. " in favor of getTdEl()", "warn", this.toString());
  11995. return this.getTdEl(index);
  11996. },
  11997. /**
  11998. * @method getRow
  11999. * @deprecated Use getTrEl().
  12000. */
  12001. getRow : function(index) {
  12002. // Backward compatibility
  12003. YAHOO.log("The method getRow() has been deprecated" +
  12004. " in favor of getTrEl()", "warn", this.toString());
  12005. return this.getTrEl(index);
  12006. },
  12007. /**
  12008. * @method refreshView
  12009. * @deprecated Use render.
  12010. */
  12011. refreshView : function() {
  12012. // Backward compatibility
  12013. YAHOO.log("The method refreshView() has been deprecated" +
  12014. " in favor of render()", "warn", this.toString());
  12015. this.render();
  12016. },
  12017. /**
  12018. * @method select
  12019. * @deprecated Use selectRow.
  12020. */
  12021. select : function(els) {
  12022. // Backward compatibility
  12023. YAHOO.log("The method select() has been deprecated" +
  12024. " in favor of selectRow()", "warn", this.toString());
  12025. if(!lang.isArray(els)) {
  12026. els = [els];
  12027. }
  12028. for(var i=0; i<els.length; i++) {
  12029. this.selectRow(els[i]);
  12030. }
  12031. },
  12032. /**
  12033. * @method onEventEditCell
  12034. * @deprecated Use onEventShowCellEditor.
  12035. */
  12036. onEventEditCell : function(oArgs) {
  12037. // Backward compatibility
  12038. YAHOO.log("The method onEventEditCell() has been deprecated" +
  12039. " in favor of onEventShowCellEditor()", "warn", this.toString());
  12040. this.onEventShowCellEditor(oArgs);
  12041. },
  12042. /**
  12043. * @method _syncColWidths
  12044. * @deprecated Use validateColumnWidths.
  12045. */
  12046. _syncColWidths : function() {
  12047. // Backward compatibility
  12048. YAHOO.log("The method _syncColWidths() has been deprecated" +
  12049. " in favor of validateColumnWidths()", "warn", this.toString());
  12050. this.validateColumnWidths();
  12051. }
  12052. /**
  12053. * @event headerRowMouseoverEvent
  12054. * @deprecated Use theadRowMouseoverEvent.
  12055. */
  12056. /**
  12057. * @event headerRowMouseoutEvent
  12058. * @deprecated Use theadRowMouseoutEvent.
  12059. */
  12060. /**
  12061. * @event headerRowMousedownEvent
  12062. * @deprecated Use theadRowMousedownEvent.
  12063. */
  12064. /**
  12065. * @event headerRowClickEvent
  12066. * @deprecated Use theadRowClickEvent.
  12067. */
  12068. /**
  12069. * @event headerRowDblclickEvent
  12070. * @deprecated Use theadRowDblclickEvent.
  12071. */
  12072. /**
  12073. * @event headerCellMouseoverEvent
  12074. * @deprecated Use theadCellMouseoverEvent.
  12075. */
  12076. /**
  12077. * @event headerCellMouseoutEvent
  12078. * @deprecated Use theadCellMouseoutEvent.
  12079. */
  12080. /**
  12081. * @event headerCellMousedownEvent
  12082. * @deprecated Use theadCellMousedownEvent.
  12083. */
  12084. /**
  12085. * @event headerCellClickEvent
  12086. * @deprecated Use theadCellClickEvent.
  12087. */
  12088. /**
  12089. * @event headerCellDblclickEvent
  12090. * @deprecated Use theadCellDblclickEvent.
  12091. */
  12092. /**
  12093. * @event headerLabelMouseoverEvent
  12094. * @deprecated Use theadLabelMouseoverEvent.
  12095. */
  12096. /**
  12097. * @event headerLabelMouseoutEvent
  12098. * @deprecated Use theadLabelMouseoutEvent.
  12099. */
  12100. /**
  12101. * @event headerLabelMousedownEvent
  12102. * @deprecated Use theadLabelMousedownEvent.
  12103. */
  12104. /**
  12105. * @event headerLabelClickEvent
  12106. * @deprecated Use theadLabelClickEvent.
  12107. */
  12108. /**
  12109. * @event headerLabelDbllickEvent
  12110. * @deprecated Use theadLabelDblclickEvent.
  12111. */
  12112. });
  12113. /**
  12114. * Alias for onDataReturnSetRows for backward compatibility
  12115. * @method onDataReturnSetRecords
  12116. * @deprecated Use onDataReturnSetRows
  12117. */
  12118. DT.prototype.onDataReturnSetRecords = DT.prototype.onDataReturnSetRows;
  12119. /**
  12120. * Alias for onPaginatorChange for backward compatibility
  12121. * @method onPaginatorChange
  12122. * @deprecated Use onPaginatorChangeRequest
  12123. */
  12124. DT.prototype.onPaginatorChange = DT.prototype.onPaginatorChangeRequest;
  12125. /////////////////////////////////////////////////////////////////////////////
  12126. //
  12127. // Deprecated static APIs
  12128. //
  12129. /////////////////////////////////////////////////////////////////////////////
  12130. /**
  12131. * @method DataTable.formatTheadCell
  12132. * @deprecated Use formatTheadCell.
  12133. */
  12134. DT.formatTheadCell = function() {};
  12135. /**
  12136. * @method DataTable.editCheckbox
  12137. * @deprecated Use YAHOO.widget.CheckboxCellEditor.
  12138. */
  12139. DT.editCheckbox = function() {};
  12140. /**
  12141. * @method DataTable.editDate
  12142. * @deprecated Use YAHOO.widget.DateCellEditor.
  12143. */
  12144. DT.editDate = function() {};
  12145. /**
  12146. * @method DataTable.editDropdown
  12147. * @deprecated Use YAHOO.widget.DropdownCellEditor.
  12148. */
  12149. DT.editDropdown = function() {};
  12150. /**
  12151. * @method DataTable.editRadio
  12152. * @deprecated Use YAHOO.widget.RadioCellEditor.
  12153. */
  12154. DT.editRadio = function() {};
  12155. /**
  12156. * @method DataTable.editTextarea
  12157. * @deprecated Use YAHOO.widget.TextareaCellEditor
  12158. */
  12159. DT.editTextarea = function() {};
  12160. /**
  12161. * @method DataTable.editTextbox
  12162. * @deprecated Use YAHOO.widget.TextboxCellEditor
  12163. */
  12164. DT.editTextbox= function() {};
  12165. })();
  12166. (function () {
  12167. var lang = YAHOO.lang,
  12168. util = YAHOO.util,
  12169. widget = YAHOO.widget,
  12170. ua = YAHOO.env.ua,
  12171. Dom = util.Dom,
  12172. Ev = util.Event,
  12173. DS = util.DataSourceBase,
  12174. DT = widget.DataTable,
  12175. Pag = widget.Paginator;
  12176. /**
  12177. * The ScrollingDataTable class extends the DataTable class to provide
  12178. * functionality for x-scrolling, y-scrolling, and xy-scrolling.
  12179. *
  12180. * @namespace YAHOO.widget
  12181. * @class ScrollingDataTable
  12182. * @extends YAHOO.widget.DataTable
  12183. * @constructor
  12184. * @param elContainer {HTMLElement} Container element for the TABLE.
  12185. * @param aColumnDefs {Object[]} Array of object literal Column definitions.
  12186. * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
  12187. * @param oConfigs {object} (optional) Object literal of configuration values.
  12188. */
  12189. widget.ScrollingDataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
  12190. oConfigs = oConfigs || {};
  12191. // Prevent infinite loop
  12192. if(oConfigs.scrollable) {
  12193. oConfigs.scrollable = false;
  12194. }
  12195. widget.ScrollingDataTable.superclass.constructor.call(this, elContainer,aColumnDefs,oDataSource,oConfigs);
  12196. // Once per instance
  12197. this.subscribe("columnShowEvent", this._onColumnChange);
  12198. };
  12199. var SDT = widget.ScrollingDataTable;
  12200. /////////////////////////////////////////////////////////////////////////////
  12201. //
  12202. // Public constants
  12203. //
  12204. /////////////////////////////////////////////////////////////////////////////
  12205. lang.augmentObject(SDT, {
  12206. /**
  12207. * Class name assigned to inner DataTable header container.
  12208. *
  12209. * @property DataTable.CLASS_HEADER
  12210. * @type String
  12211. * @static
  12212. * @final
  12213. * @default "yui-dt-hd"
  12214. */
  12215. CLASS_HEADER : "yui-dt-hd",
  12216. /**
  12217. * Class name assigned to inner DataTable body container.
  12218. *
  12219. * @property DataTable.CLASS_BODY
  12220. * @type String
  12221. * @static
  12222. * @final
  12223. * @default "yui-dt-bd"
  12224. */
  12225. CLASS_BODY : "yui-dt-bd"
  12226. });
  12227. lang.extend(SDT, DT, {
  12228. /**
  12229. * Container for fixed header TABLE element.
  12230. *
  12231. * @property _elHdContainer
  12232. * @type HTMLElement
  12233. * @private
  12234. */
  12235. _elHdContainer : null,
  12236. /**
  12237. * Fixed header TABLE element.
  12238. *
  12239. * @property _elHdTable
  12240. * @type HTMLElement
  12241. * @private
  12242. */
  12243. _elHdTable : null,
  12244. /**
  12245. * Container for scrolling body TABLE element.
  12246. *
  12247. * @property _elBdContainer
  12248. * @type HTMLElement
  12249. * @private
  12250. */
  12251. _elBdContainer : null,
  12252. /**
  12253. * Body THEAD element.
  12254. *
  12255. * @property _elBdThead
  12256. * @type HTMLElement
  12257. * @private
  12258. */
  12259. _elBdThead : null,
  12260. /**
  12261. * Offscreen container to temporarily clone SDT for auto-width calculation.
  12262. *
  12263. * @property _elTmpContainer
  12264. * @type HTMLElement
  12265. * @private
  12266. */
  12267. _elTmpContainer : null,
  12268. /**
  12269. * Offscreen TABLE element for auto-width calculation.
  12270. *
  12271. * @property _elTmpTable
  12272. * @type HTMLElement
  12273. * @private
  12274. */
  12275. _elTmpTable : null,
  12276. /**
  12277. * True if x-scrollbar is currently visible.
  12278. * @property _bScrollbarX
  12279. * @type Boolean
  12280. * @private
  12281. */
  12282. _bScrollbarX : null,
  12283. /////////////////////////////////////////////////////////////////////////////
  12284. //
  12285. // Superclass methods
  12286. //
  12287. /////////////////////////////////////////////////////////////////////////////
  12288. /**
  12289. * Implementation of Element's abstract method. Sets up config values.
  12290. *
  12291. * @method initAttributes
  12292. * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
  12293. * @private
  12294. */
  12295. initAttributes : function(oConfigs) {
  12296. oConfigs = oConfigs || {};
  12297. SDT.superclass.initAttributes.call(this, oConfigs);
  12298. /**
  12299. * @attribute width
  12300. * @description Table width for scrollable tables (e.g., "40em").
  12301. * @type String
  12302. */
  12303. this.setAttributeConfig("width", {
  12304. value: null,
  12305. validator: lang.isString,
  12306. method: function(oParam) {
  12307. if(this._elHdContainer && this._elBdContainer) {
  12308. this._elHdContainer.style.width = oParam;
  12309. this._elBdContainer.style.width = oParam;
  12310. this._syncScrollX();
  12311. this._syncScrollOverhang();
  12312. }
  12313. }
  12314. });
  12315. /**
  12316. * @attribute height
  12317. * @description Table body height for scrollable tables, not including headers (e.g., "40em").
  12318. * @type String
  12319. */
  12320. this.setAttributeConfig("height", {
  12321. value: null,
  12322. validator: lang.isString,
  12323. method: function(oParam) {
  12324. if(this._elHdContainer && this._elBdContainer) {
  12325. this._elBdContainer.style.height = oParam;
  12326. this._syncScrollX();
  12327. this._syncScrollY();
  12328. this._syncScrollOverhang();
  12329. }
  12330. }
  12331. });
  12332. /**
  12333. * @attribute COLOR_COLUMNFILLER
  12334. * @description CSS color value assigned to header filler on scrollable tables.
  12335. * @type String
  12336. * @default "#F2F2F2"
  12337. */
  12338. this.setAttributeConfig("COLOR_COLUMNFILLER", {
  12339. value: "#F2F2F2",
  12340. validator: lang.isString,
  12341. method: function(oParam) {
  12342. this._elHdContainer.style.backgroundColor = oParam;
  12343. }
  12344. });
  12345. },
  12346. /**
  12347. * Initializes DOM elements for a ScrollingDataTable, including creation of
  12348. * two separate TABLE elements.
  12349. *
  12350. * @method _initDomElements
  12351. * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
  12352. * return {Boolean} False in case of error, otherwise true
  12353. * @private
  12354. */
  12355. _initDomElements : function(elContainer) {
  12356. // Outer and inner containers
  12357. this._initContainerEl(elContainer);
  12358. if(this._elContainer && this._elHdContainer && this._elBdContainer) {
  12359. // TABLEs
  12360. this._initTableEl();
  12361. if(this._elHdTable && this._elTable) {
  12362. // COLGROUPs
  12363. ///this._initColgroupEl(this._elHdTable, this._elTable);
  12364. this._initColgroupEl(this._elHdTable);
  12365. // THEADs
  12366. this._initTheadEl(this._elHdTable, this._elTable);
  12367. // Primary TBODY
  12368. this._initTbodyEl(this._elTable);
  12369. // Message TBODY
  12370. this._initMsgTbodyEl(this._elTable);
  12371. }
  12372. }
  12373. if(!this._elContainer || !this._elTable || !this._elColgroup || !this._elThead || !this._elTbody || !this._elMsgTbody ||
  12374. !this._elHdTable || !this._elBdThead) {
  12375. YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString());
  12376. return false;
  12377. }
  12378. else {
  12379. return true;
  12380. }
  12381. },
  12382. /**
  12383. * Destroy's the DataTable outer and inner container elements, if available.
  12384. *
  12385. * @method _destroyContainerEl
  12386. * @param elContainer {HTMLElement} Reference to the container element.
  12387. * @private
  12388. */
  12389. _destroyContainerEl : function(elContainer) {
  12390. Dom.removeClass(elContainer, DT.CLASS_SCROLLABLE);
  12391. SDT.superclass._destroyContainerEl.call(this, elContainer);
  12392. this._elHdContainer = null;
  12393. this._elBdContainer = null;
  12394. },
  12395. /**
  12396. * Initializes the DataTable outer container element and creates inner header
  12397. * and body container elements.
  12398. *
  12399. * @method _initContainerEl
  12400. * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
  12401. * @private
  12402. */
  12403. _initContainerEl : function(elContainer) {
  12404. SDT.superclass._initContainerEl.call(this, elContainer);
  12405. if(this._elContainer) {
  12406. elContainer = this._elContainer; // was constructor input, now is DOM ref
  12407. Dom.addClass(elContainer, DT.CLASS_SCROLLABLE);
  12408. // Container for header TABLE
  12409. var elHdContainer = document.createElement("div");
  12410. elHdContainer.style.width = this.get("width") || "";
  12411. elHdContainer.style.backgroundColor = this.get("COLOR_COLUMNFILLER");
  12412. Dom.addClass(elHdContainer, SDT.CLASS_HEADER);
  12413. this._elHdContainer = elHdContainer;
  12414. elContainer.appendChild(elHdContainer);
  12415. // Container for body TABLE
  12416. var elBdContainer = document.createElement("div");
  12417. elBdContainer.style.width = this.get("width") || "";
  12418. elBdContainer.style.height = this.get("height") || "";
  12419. Dom.addClass(elBdContainer, SDT.CLASS_BODY);
  12420. Ev.addListener(elBdContainer, "scroll", this._onScroll, this); // to sync horiz scroll headers
  12421. this._elBdContainer = elBdContainer;
  12422. elContainer.appendChild(elBdContainer);
  12423. }
  12424. },
  12425. /**
  12426. * Creates HTML markup CAPTION element.
  12427. *
  12428. * @method _initCaptionEl
  12429. * @param sCaption {String} Text for caption.
  12430. * @private
  12431. */
  12432. _initCaptionEl : function(sCaption) {
  12433. // Not yet supported
  12434. /*if(this._elHdTable && sCaption) {
  12435. // Create CAPTION element
  12436. if(!this._elCaption) {
  12437. this._elCaption = this._elHdTable.createCaption();
  12438. }
  12439. // Set CAPTION value
  12440. this._elCaption.innerHTML = sCaption;
  12441. }
  12442. else if(this._elCaption) {
  12443. this._elCaption.parentNode.removeChild(this._elCaption);
  12444. }*/
  12445. },
  12446. /**
  12447. * Destroy's the DataTable head TABLE element, if available.
  12448. *
  12449. * @method _destroyHdTableEl
  12450. * @private
  12451. */
  12452. _destroyHdTableEl : function() {
  12453. var elTable = this._elHdTable;
  12454. if(elTable) {
  12455. Ev.purgeElement(elTable, true);
  12456. elTable.parentNode.removeChild(elTable);
  12457. // A little out of place, but where else can we null out these extra elements?
  12458. ///this._elBdColgroup = null;
  12459. this._elBdThead = null;
  12460. }
  12461. },
  12462. /**
  12463. * Initializes ScrollingDataTable TABLE elements into the two inner containers.
  12464. *
  12465. * @method _initTableEl
  12466. * @private
  12467. */
  12468. _initTableEl : function() {
  12469. // Head TABLE
  12470. if(this._elHdContainer) {
  12471. this._destroyHdTableEl();
  12472. // Create TABLE
  12473. this._elHdTable = this._elHdContainer.appendChild(document.createElement("table"));
  12474. }
  12475. // Body TABLE
  12476. SDT.superclass._initTableEl.call(this, this._elBdContainer);
  12477. },
  12478. /**
  12479. * Initializes ScrollingDataTable THEAD elements into the two inner containers.
  12480. *
  12481. * @method _initTheadEl
  12482. * @param elHdTable {HTMLElement} (optional) Fixed header TABLE element reference.
  12483. * @param elTable {HTMLElement} (optional) TABLE element reference.
  12484. * @private
  12485. */
  12486. _initTheadEl : function(elHdTable, elTable) {
  12487. elHdTable = elHdTable || this._elHdTable;
  12488. elTable = elTable || this._elTable;
  12489. // Scrolling body's THEAD
  12490. this._initBdTheadEl(elTable);
  12491. // Standard fixed head THEAD
  12492. SDT.superclass._initTheadEl.call(this, elHdTable);
  12493. },
  12494. /**
  12495. * SDT changes ID so as not to duplicate the accessibility TH IDs.
  12496. *
  12497. * @method _initThEl
  12498. * @param elTh {HTMLElement} TH element reference.
  12499. * @param oColumn {YAHOO.widget.Column} Column object.
  12500. * @private
  12501. */
  12502. _initThEl : function(elTh, oColumn) {
  12503. SDT.superclass._initThEl.call(this, elTh, oColumn);
  12504. elTh.id = this.getId() +"-fixedth-" + oColumn.getSanitizedKey(); // Needed for getColumn by TH and ColumnDD
  12505. },
  12506. /**
  12507. * Destroy's the DataTable body THEAD element, if available.
  12508. *
  12509. * @method _destroyBdTheadEl
  12510. * @private
  12511. */
  12512. _destroyBdTheadEl : function() {
  12513. var elBdThead = this._elBdThead;
  12514. if(elBdThead) {
  12515. var elTable = elBdThead.parentNode;
  12516. Ev.purgeElement(elBdThead, true);
  12517. elTable.removeChild(elBdThead);
  12518. this._elBdThead = null;
  12519. this._destroyColumnHelpers();
  12520. }
  12521. },
  12522. /**
  12523. * Initializes body THEAD element.
  12524. *
  12525. * @method _initBdTheadEl
  12526. * @param elTable {HTMLElement} TABLE element into which to create THEAD.
  12527. * @return {HTMLElement} Initialized THEAD element.
  12528. * @private
  12529. */
  12530. _initBdTheadEl : function(elTable) {
  12531. if(elTable) {
  12532. // Destroy previous
  12533. this._destroyBdTheadEl();
  12534. var elThead = elTable.insertBefore(document.createElement("thead"), elTable.firstChild);
  12535. // Add TRs to the THEAD;
  12536. var oColumnSet = this._oColumnSet,
  12537. colTree = oColumnSet.tree,
  12538. elTh, elTheadTr, oColumn, i, j, k, len;
  12539. for(i=0, k=colTree.length; i<k; i++) {
  12540. elTheadTr = elThead.appendChild(document.createElement("tr"));
  12541. // ...and create TH cells
  12542. for(j=0, len=colTree[i].length; j<len; j++) {
  12543. oColumn = colTree[i][j];
  12544. elTh = elTheadTr.appendChild(document.createElement("th"));
  12545. this._initBdThEl(elTh,oColumn,i,j);
  12546. }
  12547. }
  12548. this._elBdThead = elThead;
  12549. YAHOO.log("Accessibility TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString());
  12550. }
  12551. },
  12552. /**
  12553. * Populates TH element for the body THEAD element.
  12554. *
  12555. * @method _initBdThEl
  12556. * @param elTh {HTMLElement} TH element reference.
  12557. * @param oColumn {YAHOO.widget.Column} Column object.
  12558. * @private
  12559. */
  12560. _initBdThEl : function(elTh, oColumn) {
  12561. elTh.id = this.getId()+"-th-" + oColumn.getSanitizedKey(); // Needed for accessibility
  12562. elTh.rowSpan = oColumn.getRowspan();
  12563. elTh.colSpan = oColumn.getColspan();
  12564. // Assign abbr attribute
  12565. if(oColumn.abbr) {
  12566. elTh.abbr = oColumn.abbr;
  12567. }
  12568. // TODO: strip links and form elements
  12569. var sKey = oColumn.getKey();
  12570. var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
  12571. elTh.innerHTML = sLabel;
  12572. },
  12573. /**
  12574. * Initializes ScrollingDataTable TBODY element for data
  12575. *
  12576. * @method _initTbodyEl
  12577. * @param elTable {HTMLElement} TABLE element into which to create TBODY .
  12578. * @private
  12579. */
  12580. _initTbodyEl : function(elTable) {
  12581. SDT.superclass._initTbodyEl.call(this, elTable);
  12582. // Bug 2105534 - Safari 3 gap
  12583. // Bug 2492591 - IE8 offsetTop
  12584. elTable.style.marginTop = (this._elTbody.offsetTop > 0) ?
  12585. "-"+this._elTbody.offsetTop+"px" : 0;
  12586. },
  12587. /**
  12588. * Sets focus on the given element.
  12589. *
  12590. * @method _focusEl
  12591. * @param el {HTMLElement} Element.
  12592. * @private
  12593. */
  12594. _focusEl : function(el) {
  12595. el = el || this._elTbody;
  12596. var oSelf = this;
  12597. this._storeScrollPositions();
  12598. // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
  12599. // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
  12600. // strange unexpected things as the user clicks on buttons and other controls.
  12601. // Bug 1921135: Wrap the whole thing in a setTimeout
  12602. setTimeout(function() {
  12603. setTimeout(function() {
  12604. try {
  12605. el.focus();
  12606. oSelf._restoreScrollPositions();
  12607. }
  12608. catch(e) {
  12609. }
  12610. },0);
  12611. }, 0);
  12612. },
  12613. /**
  12614. * Internal wrapper calls run() on render Chain instance.
  12615. *
  12616. * @method _runRenderChain
  12617. * @private
  12618. */
  12619. _runRenderChain : function() {
  12620. this._storeScrollPositions();
  12621. this._oChainRender.run();
  12622. },
  12623. /**
  12624. * Stores scroll positions so they can be restored after a render.
  12625. *
  12626. * @method _storeScrollPositions
  12627. * @private
  12628. */
  12629. _storeScrollPositions : function() {
  12630. this._nScrollTop = this._elBdContainer.scrollTop;
  12631. this._nScrollLeft = this._elBdContainer.scrollLeft;
  12632. },
  12633. /**
  12634. * Clears stored scroll positions to interrupt the automatic restore mechanism.
  12635. * Useful for setting scroll positions programmatically rather than as part of
  12636. * the post-render cleanup process.
  12637. *
  12638. * @method clearScrollPositions
  12639. * @private
  12640. */
  12641. clearScrollPositions : function() {
  12642. this._nScrollTop = 0;
  12643. this._nScrollLeft = 0;
  12644. },
  12645. /**
  12646. * Restores scroll positions to stored value.
  12647. *
  12648. * @method _retoreScrollPositions
  12649. * @private
  12650. */
  12651. _restoreScrollPositions : function() {
  12652. // Reset scroll positions
  12653. if(this._nScrollTop) {
  12654. this._elBdContainer.scrollTop = this._nScrollTop;
  12655. this._nScrollTop = null;
  12656. }
  12657. if(this._nScrollLeft) {
  12658. this._elBdContainer.scrollLeft = this._nScrollLeft;
  12659. this._nScrollLeft = null;
  12660. }
  12661. },
  12662. /**
  12663. * Helper function calculates and sets a validated width for a Column in a ScrollingDataTable.
  12664. *
  12665. * @method _validateColumnWidth
  12666. * @param oColumn {YAHOO.widget.Column} Column instance.
  12667. * @param elTd {HTMLElement} TD element to validate against.
  12668. * @private
  12669. */
  12670. _validateColumnWidth : function(oColumn, elTd) {
  12671. // Only Columns without widths that are not hidden
  12672. if(!oColumn.width && !oColumn.hidden) {
  12673. var elTh = oColumn.getThEl();
  12674. // Unset a calculated auto-width
  12675. if(oColumn._calculatedWidth) {
  12676. this._setColumnWidth(oColumn, "auto", "visible");
  12677. }
  12678. // Compare auto-widths
  12679. if(elTh.offsetWidth !== elTd.offsetWidth) {
  12680. var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
  12681. oColumn.getThLinerEl() : elTd.firstChild;
  12682. // Grab the wider liner width, unless the minWidth is wider
  12683. var newWidth = Math.max(0,
  12684. (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
  12685. oColumn.minWidth);
  12686. var sOverflow = 'visible';
  12687. // Now validate against maxAutoWidth
  12688. if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
  12689. newWidth = oColumn.maxAutoWidth;
  12690. sOverflow = "hidden";
  12691. }
  12692. // Set to the wider auto-width
  12693. this._elTbody.style.display = "none";
  12694. this._setColumnWidth(oColumn, newWidth+'px', sOverflow);
  12695. oColumn._calculatedWidth = newWidth;
  12696. this._elTbody.style.display = "";
  12697. }
  12698. }
  12699. },
  12700. /**
  12701. * For one or all Columns of a ScrollingDataTable, when Column is not hidden,
  12702. * and width is not set, syncs widths of header and body cells and
  12703. * validates that width against minWidth and/or maxAutoWidth as necessary.
  12704. *
  12705. * @method validateColumnWidths
  12706. * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
  12707. */
  12708. validateColumnWidths : function(oColumn) {
  12709. // Validate there is at least one TR with proper TDs
  12710. var allKeys = this._oColumnSet.keys,
  12711. allKeysLength = allKeys.length,
  12712. elRow = this.getFirstTrEl();
  12713. // Reset overhang for IE
  12714. if(ua.ie) {
  12715. this._setOverhangValue(1);
  12716. }
  12717. if(allKeys && elRow && (elRow.childNodes.length === allKeysLength)) {
  12718. // Temporarily unsnap container since it causes inaccurate calculations
  12719. var sWidth = this.get("width");
  12720. if(sWidth) {
  12721. this._elHdContainer.style.width = "";
  12722. this._elBdContainer.style.width = "";
  12723. }
  12724. this._elContainer.style.width = "";
  12725. //Validate just one Column
  12726. if(oColumn && lang.isNumber(oColumn.getKeyIndex())) {
  12727. this._validateColumnWidth(oColumn, elRow.childNodes[oColumn.getKeyIndex()]);
  12728. }
  12729. // Iterate through all Columns to unset calculated widths in one pass
  12730. else {
  12731. var elTd, todos = [], thisTodo, i, len;
  12732. for(i=0; i<allKeysLength; i++) {
  12733. oColumn = allKeys[i];
  12734. // Only Columns without widths that are not hidden, unset a calculated auto-width
  12735. if(!oColumn.width && !oColumn.hidden && oColumn._calculatedWidth) {
  12736. todos[todos.length] = oColumn;
  12737. }
  12738. }
  12739. this._elTbody.style.display = "none";
  12740. for(i=0, len=todos.length; i<len; i++) {
  12741. this._setColumnWidth(todos[i], "auto", "visible");
  12742. }
  12743. this._elTbody.style.display = "";
  12744. todos = [];
  12745. // Iterate through all Columns and make the store the adjustments to make in one pass
  12746. for(i=0; i<allKeysLength; i++) {
  12747. oColumn = allKeys[i];
  12748. elTd = elRow.childNodes[i];
  12749. // Only Columns without widths that are not hidden
  12750. if(!oColumn.width && !oColumn.hidden) {
  12751. var elTh = oColumn.getThEl();
  12752. // Compare auto-widths
  12753. if(elTh.offsetWidth !== elTd.offsetWidth) {
  12754. var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
  12755. oColumn.getThLinerEl() : elTd.firstChild;
  12756. // Grab the wider liner width, unless the minWidth is wider
  12757. var newWidth = Math.max(0,
  12758. (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
  12759. oColumn.minWidth);
  12760. var sOverflow = 'visible';
  12761. // Now validate against maxAutoWidth
  12762. if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
  12763. newWidth = oColumn.maxAutoWidth;
  12764. sOverflow = "hidden";
  12765. }
  12766. todos[todos.length] = [oColumn, newWidth, sOverflow];
  12767. }
  12768. }
  12769. }
  12770. this._elTbody.style.display = "none";
  12771. for(i=0, len=todos.length; i<len; i++) {
  12772. thisTodo = todos[i];
  12773. // Set to the wider auto-width
  12774. this._setColumnWidth(thisTodo[0], thisTodo[1]+"px", thisTodo[2]);
  12775. thisTodo[0]._calculatedWidth = thisTodo[1];
  12776. }
  12777. this._elTbody.style.display = "";
  12778. }
  12779. // Resnap unsnapped containers
  12780. if(sWidth) {
  12781. this._elHdContainer.style.width = sWidth;
  12782. this._elBdContainer.style.width = sWidth;
  12783. }
  12784. }
  12785. this._syncScroll();
  12786. this._restoreScrollPositions();
  12787. },
  12788. /**
  12789. * Syncs padding around scrollable tables, including Column header right-padding
  12790. * and container width and height.
  12791. *
  12792. * @method _syncScroll
  12793. * @private
  12794. */
  12795. _syncScroll : function() {
  12796. this._syncScrollX();
  12797. this._syncScrollY();
  12798. this._syncScrollOverhang();
  12799. if(ua.opera) {
  12800. // Bug 1925874
  12801. this._elHdContainer.scrollLeft = this._elBdContainer.scrollLeft;
  12802. if(!this.get("width")) {
  12803. // Bug 1926125
  12804. document.body.style += '';
  12805. }
  12806. }
  12807. },
  12808. /**
  12809. * Snaps container width for y-scrolling tables.
  12810. *
  12811. * @method _syncScrollY
  12812. * @private
  12813. */
  12814. _syncScrollY : function() {
  12815. var elTbody = this._elTbody,
  12816. elBdContainer = this._elBdContainer;
  12817. // X-scrolling not enabled
  12818. if(!this.get("width")) {
  12819. // Snap outer container width to content
  12820. this._elContainer.style.width =
  12821. (elBdContainer.scrollHeight > elBdContainer.clientHeight) ?
  12822. // but account for y-scrollbar since it is visible
  12823. (elTbody.parentNode.clientWidth + 19) + "px" :
  12824. // no y-scrollbar, just borders
  12825. (elTbody.parentNode.clientWidth + 2) + "px";
  12826. }
  12827. },
  12828. /**
  12829. * Snaps container height for x-scrolling tables in IE. Syncs message TBODY width.
  12830. *
  12831. * @method _syncScrollX
  12832. * @private
  12833. */
  12834. _syncScrollX : function() {
  12835. var elTbody = this._elTbody,
  12836. elBdContainer = this._elBdContainer;
  12837. // IE 6 and 7 only when y-scrolling not enabled
  12838. if(!this.get("height") && (ua.ie)) {
  12839. // Snap outer container height to content
  12840. elBdContainer.style.height =
  12841. // but account for x-scrollbar if it is visible
  12842. (elBdContainer.scrollWidth > elBdContainer.offsetWidth ) ?
  12843. (elTbody.parentNode.offsetHeight + 18) + "px" :
  12844. elTbody.parentNode.offsetHeight + "px";
  12845. }
  12846. // Sync message tbody
  12847. if(this._elTbody.rows.length === 0) {
  12848. this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
  12849. }
  12850. else {
  12851. this._elMsgTbody.parentNode.style.width = "";
  12852. }
  12853. },
  12854. /**
  12855. * Adds/removes Column header overhang as necesary.
  12856. *
  12857. * @method _syncScrollOverhang
  12858. * @private
  12859. */
  12860. _syncScrollOverhang : function() {
  12861. var elBdContainer = this._elBdContainer,
  12862. // Overhang should be either 1 (default) or 18px, depending on the location of the right edge of the table
  12863. nPadding = 1;
  12864. // Y-scrollbar is visible, which is when the overhang needs to jut out
  12865. if((elBdContainer.scrollHeight > elBdContainer.clientHeight) &&
  12866. // X-scrollbar is also visible, which means the right is jagged, not flush with the Column
  12867. (elBdContainer.scrollWidth > elBdContainer.clientWidth)) {
  12868. nPadding = 18;
  12869. }
  12870. this._setOverhangValue(nPadding);
  12871. },
  12872. /**
  12873. * Sets Column header overhang to given width.
  12874. *
  12875. * @method _setOverhangValue
  12876. * @param nBorderWidth {Number} Value of new border for overhang.
  12877. * @private
  12878. */
  12879. _setOverhangValue : function(nBorderWidth) {
  12880. var aLastHeaders = this._oColumnSet.headers[this._oColumnSet.headers.length-1] || [],
  12881. len = aLastHeaders.length,
  12882. sPrefix = this._sId+"-fixedth-",
  12883. sValue = nBorderWidth + "px solid " + this.get("COLOR_COLUMNFILLER");
  12884. this._elThead.style.display = "none";
  12885. for(var i=0; i<len; i++) {
  12886. Dom.get(sPrefix+aLastHeaders[i]).style.borderRight = sValue;
  12887. }
  12888. this._elThead.style.display = "";
  12889. },
  12890. /**
  12891. * Returns DOM reference to the DataTable's fixed header container element.
  12892. *
  12893. * @method getHdContainerEl
  12894. * @return {HTMLElement} Reference to DIV element.
  12895. */
  12896. getHdContainerEl : function() {
  12897. return this._elHdContainer;
  12898. },
  12899. /**
  12900. * Returns DOM reference to the DataTable's scrolling body container element.
  12901. *
  12902. * @method getBdContainerEl
  12903. * @return {HTMLElement} Reference to DIV element.
  12904. */
  12905. getBdContainerEl : function() {
  12906. return this._elBdContainer;
  12907. },
  12908. /**
  12909. * Returns DOM reference to the DataTable's fixed header TABLE element.
  12910. *
  12911. * @method getHdTableEl
  12912. * @return {HTMLElement} Reference to TABLE element.
  12913. */
  12914. getHdTableEl : function() {
  12915. return this._elHdTable;
  12916. },
  12917. /**
  12918. * Returns DOM reference to the DataTable's scrolling body TABLE element.
  12919. *
  12920. * @method getBdTableEl
  12921. * @return {HTMLElement} Reference to TABLE element.
  12922. */
  12923. getBdTableEl : function() {
  12924. return this._elTable;
  12925. },
  12926. /**
  12927. * Disables ScrollingDataTable UI.
  12928. *
  12929. * @method disable
  12930. */
  12931. disable : function() {
  12932. var elMask = this._elMask;
  12933. elMask.style.width = this._elBdContainer.offsetWidth + "px";
  12934. elMask.style.height = this._elHdContainer.offsetHeight + this._elBdContainer.offsetHeight + "px";
  12935. elMask.style.display = "";
  12936. this.fireEvent("disableEvent");
  12937. },
  12938. /**
  12939. * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
  12940. * non-nested Columns, and top-level parent Columns (which will remove all
  12941. * children Columns).
  12942. *
  12943. * @method removeColumn
  12944. * @param oColumn {YAHOO.widget.Column} Column instance.
  12945. * @return oColumn {YAHOO.widget.Column} Removed Column instance.
  12946. */
  12947. removeColumn : function(oColumn) {
  12948. // Store scroll pos
  12949. var hdPos = this._elHdContainer.scrollLeft;
  12950. var bdPos = this._elBdContainer.scrollLeft;
  12951. // Call superclass method
  12952. oColumn = SDT.superclass.removeColumn.call(this, oColumn);
  12953. // Restore scroll pos
  12954. this._elHdContainer.scrollLeft = hdPos;
  12955. this._elBdContainer.scrollLeft = bdPos;
  12956. return oColumn;
  12957. },
  12958. /**
  12959. * Inserts given Column at the index if given, otherwise at the end. NOTE: You
  12960. * can only add non-nested Columns and top-level parent Columns. You cannot add
  12961. * a nested Column to an existing parent.
  12962. *
  12963. * @method insertColumn
  12964. * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
  12965. * definition or a Column instance.
  12966. * @param index {Number} (optional) New tree index.
  12967. * @return oColumn {YAHOO.widget.Column} Inserted Column instance.
  12968. */
  12969. insertColumn : function(oColumn, index) {
  12970. // Store scroll pos
  12971. var hdPos = this._elHdContainer.scrollLeft;
  12972. var bdPos = this._elBdContainer.scrollLeft;
  12973. // Call superclass method
  12974. var oNewColumn = SDT.superclass.insertColumn.call(this, oColumn, index);
  12975. // Restore scroll pos
  12976. this._elHdContainer.scrollLeft = hdPos;
  12977. this._elBdContainer.scrollLeft = bdPos;
  12978. return oNewColumn;
  12979. },
  12980. /**
  12981. * Removes given Column and inserts into given tree index. NOTE: You
  12982. * can only reorder non-nested Columns and top-level parent Columns. You cannot
  12983. * reorder a nested Column to an existing parent.
  12984. *
  12985. * @method reorderColumn
  12986. * @param oColumn {YAHOO.widget.Column} Column instance.
  12987. * @param index {Number} New tree index.
  12988. */
  12989. reorderColumn : function(oColumn, index) {
  12990. // Store scroll pos
  12991. var hdPos = this._elHdContainer.scrollLeft;
  12992. var bdPos = this._elBdContainer.scrollLeft;
  12993. // Call superclass method
  12994. var oNewColumn = SDT.superclass.reorderColumn.call(this, oColumn, index);
  12995. // Restore scroll pos
  12996. this._elHdContainer.scrollLeft = hdPos;
  12997. this._elBdContainer.scrollLeft = bdPos;
  12998. return oNewColumn;
  12999. },
  13000. /**
  13001. * Sets given Column to given pixel width. If new width is less than minWidth
  13002. * width, sets to minWidth. Updates oColumn.width value.
  13003. *
  13004. * @method setColumnWidth
  13005. * @param oColumn {YAHOO.widget.Column} Column instance.
  13006. * @param nWidth {Number} New width in pixels.
  13007. */
  13008. setColumnWidth : function(oColumn, nWidth) {
  13009. oColumn = this.getColumn(oColumn);
  13010. if(oColumn) {
  13011. this._storeScrollPositions();
  13012. // Validate new width against minWidth
  13013. if(lang.isNumber(nWidth)) {
  13014. nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
  13015. // Save state
  13016. oColumn.width = nWidth;
  13017. // Resize the DOM elements
  13018. this._setColumnWidth(oColumn, nWidth+"px");
  13019. this._syncScroll();
  13020. this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
  13021. YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString());
  13022. }
  13023. // Unsets a width to auto-size
  13024. else if(nWidth === null) {
  13025. // Save state
  13026. oColumn.width = nWidth;
  13027. // Resize the DOM elements
  13028. this._setColumnWidth(oColumn, "auto");
  13029. this.validateColumnWidths(oColumn);
  13030. this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
  13031. YAHOO.log("Column " + oColumn + " width unset", "info", this.toString());
  13032. }
  13033. // Bug 2339454: resize then sort misaligment
  13034. this._clearTrTemplateEl();
  13035. }
  13036. else {
  13037. YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString());
  13038. }
  13039. },
  13040. /**
  13041. * Scrolls to given row or cell
  13042. *
  13043. * @method scrollTo
  13044. * @param to {YAHOO.widget.Record | HTMLElement } Itme to scroll to.
  13045. */
  13046. scrollTo : function(to) {
  13047. var td = this.getTdEl(to);
  13048. if(td) {
  13049. this.clearScrollPositions();
  13050. this.getBdContainerEl().scrollLeft = td.offsetLeft;
  13051. this.getBdContainerEl().scrollTop = td.parentNode.offsetTop;
  13052. }
  13053. else {
  13054. var tr = this.getTrEl(to);
  13055. if(tr) {
  13056. this.clearScrollPositions();
  13057. this.getBdContainerEl().scrollTop = tr.offsetTop;
  13058. }
  13059. }
  13060. },
  13061. /**
  13062. * Displays message within secondary TBODY.
  13063. *
  13064. * @method showTableMessage
  13065. * @param sHTML {String} (optional) Value for innerHTMlang.
  13066. * @param sClassName {String} (optional) Classname.
  13067. */
  13068. showTableMessage : function(sHTML, sClassName) {
  13069. var elCell = this._elMsgTd;
  13070. if(lang.isString(sHTML)) {
  13071. elCell.firstChild.innerHTML = sHTML;
  13072. }
  13073. if(lang.isString(sClassName)) {
  13074. Dom.addClass(elCell.firstChild, sClassName);
  13075. }
  13076. // Needed for SDT only
  13077. var elThead = this.getTheadEl();
  13078. var elTable = elThead.parentNode;
  13079. var newWidth = elTable.offsetWidth;
  13080. this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
  13081. this._elMsgTbody.style.display = "";
  13082. this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
  13083. YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString());
  13084. },
  13085. /////////////////////////////////////////////////////////////////////////////
  13086. //
  13087. // Private Custom Event Handlers
  13088. //
  13089. /////////////////////////////////////////////////////////////////////////////
  13090. /**
  13091. * Handles Column mutations
  13092. *
  13093. * @method onColumnChange
  13094. * @param oArgs {Object} Custom Event data.
  13095. */
  13096. _onColumnChange : function(oArg) {
  13097. // Figure out which Column changed
  13098. var oColumn = (oArg.column) ? oArg.column :
  13099. (oArg.editor) ? oArg.editor.column : null;
  13100. this._storeScrollPositions();
  13101. this.validateColumnWidths(oColumn);
  13102. },
  13103. /////////////////////////////////////////////////////////////////////////////
  13104. //
  13105. // Private DOM Event Handlers
  13106. //
  13107. /////////////////////////////////////////////////////////////////////////////
  13108. /**
  13109. * Syncs scrolltop and scrollleft of all TABLEs.
  13110. *
  13111. * @method _onScroll
  13112. * @param e {HTMLEvent} The scroll event.
  13113. * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
  13114. * @private
  13115. */
  13116. _onScroll : function(e, oSelf) {
  13117. oSelf._elHdContainer.scrollLeft = oSelf._elBdContainer.scrollLeft;
  13118. if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
  13119. oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
  13120. oSelf.cancelCellEditor();
  13121. }
  13122. var elTarget = Ev.getTarget(e);
  13123. var elTag = elTarget.nodeName.toLowerCase();
  13124. oSelf.fireEvent("tableScrollEvent", {event:e, target:elTarget});
  13125. },
  13126. /**
  13127. * Handles keydown events on the THEAD element.
  13128. *
  13129. * @method _onTheadKeydown
  13130. * @param e {HTMLEvent} The key event.
  13131. * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
  13132. * @private
  13133. */
  13134. _onTheadKeydown : function(e, oSelf) {
  13135. // If tabbing to next TH label link causes THEAD to scroll,
  13136. // need to sync scrollLeft with TBODY
  13137. if(Ev.getCharCode(e) === 9) {
  13138. setTimeout(function() {
  13139. if((oSelf instanceof SDT) && oSelf._sId) {
  13140. oSelf._elBdContainer.scrollLeft = oSelf._elHdContainer.scrollLeft;
  13141. }
  13142. },0);
  13143. }
  13144. var elTarget = Ev.getTarget(e);
  13145. var elTag = elTarget.nodeName.toLowerCase();
  13146. var bKeepBubbling = true;
  13147. while(elTarget && (elTag != "table")) {
  13148. switch(elTag) {
  13149. case "body":
  13150. return;
  13151. case "input":
  13152. case "textarea":
  13153. // TODO: implement textareaKeyEvent
  13154. break;
  13155. case "thead":
  13156. bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
  13157. break;
  13158. default:
  13159. break;
  13160. }
  13161. if(bKeepBubbling === false) {
  13162. return;
  13163. }
  13164. else {
  13165. elTarget = elTarget.parentNode;
  13166. if(elTarget) {
  13167. elTag = elTarget.nodeName.toLowerCase();
  13168. }
  13169. }
  13170. }
  13171. oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
  13172. }
  13173. /**
  13174. * Fired when a fixed scrolling DataTable has a scroll.
  13175. *
  13176. * @event tableScrollEvent
  13177. * @param oArgs.event {HTMLEvent} The event object.
  13178. * @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE)
  13179. * or the DataTable's TBODY element (everyone else).
  13180. *
  13181. */
  13182. });
  13183. })();
  13184. (function () {
  13185. var lang = YAHOO.lang,
  13186. util = YAHOO.util,
  13187. widget = YAHOO.widget,
  13188. ua = YAHOO.env.ua,
  13189. Dom = util.Dom,
  13190. Ev = util.Event,
  13191. DT = widget.DataTable;
  13192. /****************************************************************************/
  13193. /****************************************************************************/
  13194. /****************************************************************************/
  13195. /**
  13196. * The BaseCellEditor class provides base functionality common to all inline cell
  13197. * editors for a DataTable widget.
  13198. *
  13199. * @namespace YAHOO.widget
  13200. * @class BaseCellEditor
  13201. * @uses YAHOO.util.EventProvider
  13202. * @constructor
  13203. * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
  13204. * @param oConfigs {Object} (Optional) Object literal of configs.
  13205. */
  13206. widget.BaseCellEditor = function(sType, oConfigs) {
  13207. this._sId = this._sId || "yui-ceditor" + YAHOO.widget.BaseCellEditor._nCount++;
  13208. this._sType = sType;
  13209. // Validate inputs
  13210. this._initConfigs(oConfigs);
  13211. // Create Custom Events
  13212. this._initEvents();
  13213. // Draw UI
  13214. this.render();
  13215. };
  13216. var BCE = widget.BaseCellEditor;
  13217. /////////////////////////////////////////////////////////////////////////////
  13218. //
  13219. // Static members
  13220. //
  13221. /////////////////////////////////////////////////////////////////////////////
  13222. lang.augmentObject(BCE, {
  13223. /**
  13224. * Global instance counter.
  13225. *
  13226. * @property CellEditor._nCount
  13227. * @type Number
  13228. * @static
  13229. * @default 0
  13230. * @private
  13231. */
  13232. _nCount : 0,
  13233. /**
  13234. * Class applied to CellEditor container.
  13235. *
  13236. * @property CellEditor.CLASS_CELLEDITOR
  13237. * @type String
  13238. * @static
  13239. * @default "yui-ceditor"
  13240. */
  13241. CLASS_CELLEDITOR : "yui-ceditor"
  13242. });
  13243. BCE.prototype = {
  13244. /////////////////////////////////////////////////////////////////////////////
  13245. //
  13246. // Private members
  13247. //
  13248. /////////////////////////////////////////////////////////////////////////////
  13249. /**
  13250. * Unique id assigned to instance "yui-ceditorN", useful prefix for generating unique
  13251. * DOM ID strings and log messages.
  13252. *
  13253. * @property _sId
  13254. * @type String
  13255. * @private
  13256. */
  13257. _sId : null,
  13258. /**
  13259. * Editor type.
  13260. *
  13261. * @property _sType
  13262. * @type String
  13263. * @private
  13264. */
  13265. _sType : null,
  13266. /**
  13267. * DataTable instance.
  13268. *
  13269. * @property _oDataTable
  13270. * @type YAHOO.widget.DataTable
  13271. * @private
  13272. */
  13273. _oDataTable : null,
  13274. /**
  13275. * Column instance.
  13276. *
  13277. * @property _oColumn
  13278. * @type YAHOO.widget.Column
  13279. * @default null
  13280. * @private
  13281. */
  13282. _oColumn : null,
  13283. /**
  13284. * Record instance.
  13285. *
  13286. * @property _oRecord
  13287. * @type YAHOO.widget.Record
  13288. * @default null
  13289. * @private
  13290. */
  13291. _oRecord : null,
  13292. /**
  13293. * TD element.
  13294. *
  13295. * @property _elTd
  13296. * @type HTMLElement
  13297. * @default null
  13298. * @private
  13299. */
  13300. _elTd : null,
  13301. /**
  13302. * Container for inline editor.
  13303. *
  13304. * @property _elContainer
  13305. * @type HTMLElement
  13306. * @private
  13307. */
  13308. _elContainer : null,
  13309. /**
  13310. * Reference to Cancel button, if available.
  13311. *
  13312. * @property _elCancelBtn
  13313. * @type HTMLElement
  13314. * @default null
  13315. * @private
  13316. */
  13317. _elCancelBtn : null,
  13318. /**
  13319. * Reference to Save button, if available.
  13320. *
  13321. * @property _elSaveBtn
  13322. * @type HTMLElement
  13323. * @default null
  13324. * @private
  13325. */
  13326. _elSaveBtn : null,
  13327. /////////////////////////////////////////////////////////////////////////////
  13328. //
  13329. // Private methods
  13330. //
  13331. /////////////////////////////////////////////////////////////////////////////
  13332. /**
  13333. * Initialize configs.
  13334. *
  13335. * @method _initConfigs
  13336. * @private
  13337. */
  13338. _initConfigs : function(oConfigs) {
  13339. // Object literal defines CellEditor configs
  13340. if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
  13341. for(var sConfig in oConfigs) {
  13342. if(sConfig) {
  13343. this[sConfig] = oConfigs[sConfig];
  13344. }
  13345. }
  13346. }
  13347. },
  13348. /**
  13349. * Initialize Custom Events.
  13350. *
  13351. * @method _initEvents
  13352. * @private
  13353. */
  13354. _initEvents : function() {
  13355. this.createEvent("showEvent");
  13356. this.createEvent("keydownEvent");
  13357. this.createEvent("invalidDataEvent");
  13358. this.createEvent("revertEvent");
  13359. this.createEvent("saveEvent");
  13360. this.createEvent("cancelEvent");
  13361. this.createEvent("blurEvent");
  13362. this.createEvent("blockEvent");
  13363. this.createEvent("unblockEvent");
  13364. },
  13365. /////////////////////////////////////////////////////////////////////////////
  13366. //
  13367. // Public properties
  13368. //
  13369. /////////////////////////////////////////////////////////////////////////////
  13370. /**
  13371. * Implementer defined function that can submit the input value to a server. This
  13372. * function must accept the arguments fnCallback and oNewValue. When the submission
  13373. * is complete, the function must also call fnCallback(bSuccess, oNewValue) to
  13374. * finish the save routine in the CellEditor. This function can also be used to
  13375. * perform extra validation or input value manipulation.
  13376. *
  13377. * @property asyncSubmitter
  13378. * @type HTMLFunction
  13379. */
  13380. asyncSubmitter : null,
  13381. /**
  13382. * Current value.
  13383. *
  13384. * @property value
  13385. * @type MIXED
  13386. */
  13387. value : null,
  13388. /**
  13389. * Default value in case Record data is undefined. NB: Null values will not trigger
  13390. * the default value.
  13391. *
  13392. * @property defaultValue
  13393. * @type MIXED
  13394. * @default null
  13395. */
  13396. defaultValue : null,
  13397. /**
  13398. * Validator function for input data, called from the DataTable instance scope,
  13399. * receives the arguments (inputValue, currentValue, editorInstance) and returns
  13400. * either the validated (or type-converted) value or undefined.
  13401. *
  13402. * @property validator
  13403. * @type HTMLFunction
  13404. * @default null
  13405. */
  13406. validator : null,
  13407. /**
  13408. * If validation is enabled, resets input field of invalid data.
  13409. *
  13410. * @property resetInvalidData
  13411. * @type Boolean
  13412. * @default true
  13413. */
  13414. resetInvalidData : true,
  13415. /**
  13416. * True if currently active.
  13417. *
  13418. * @property isActive
  13419. * @type Boolean
  13420. */
  13421. isActive : false,
  13422. /**
  13423. * Text to display on Save button.
  13424. *
  13425. * @property LABEL_SAVE
  13426. * @type String
  13427. * @default "Save"
  13428. */
  13429. LABEL_SAVE : "Save",
  13430. /**
  13431. * Text to display on Cancel button.
  13432. *
  13433. * @property LABEL_CANCEL
  13434. * @type String
  13435. * @default "Cancel"
  13436. */
  13437. LABEL_CANCEL : "Cancel",
  13438. /**
  13439. * True if Save/Cancel buttons should not be displayed in the CellEditor.
  13440. *
  13441. * @property disableBtns
  13442. * @type Boolean
  13443. * @default false
  13444. */
  13445. disableBtns : false,
  13446. /////////////////////////////////////////////////////////////////////////////
  13447. //
  13448. // Public methods
  13449. //
  13450. /////////////////////////////////////////////////////////////////////////////
  13451. /**
  13452. * CellEditor instance name, for logging.
  13453. *
  13454. * @method toString
  13455. * @return {String} Unique name of the CellEditor instance.
  13456. */
  13457. toString : function() {
  13458. return "CellEditor instance " + this._sId;
  13459. },
  13460. /**
  13461. * CellEditor unique ID.
  13462. *
  13463. * @method getId
  13464. * @return {String} Unique ID of the CellEditor instance.
  13465. */
  13466. getId : function() {
  13467. return this._sId;
  13468. },
  13469. /**
  13470. * Returns reference to associated DataTable instance.
  13471. *
  13472. * @method getDataTable
  13473. * @return {YAHOO.widget.DataTable} DataTable instance.
  13474. */
  13475. getDataTable : function() {
  13476. return this._oDataTable;
  13477. },
  13478. /**
  13479. * Returns reference to associated Column instance.
  13480. *
  13481. * @method getColumn
  13482. * @return {YAHOO.widget.Column} Column instance.
  13483. */
  13484. getColumn : function() {
  13485. return this._oColumn;
  13486. },
  13487. /**
  13488. * Returns reference to associated Record instance.
  13489. *
  13490. * @method getRecord
  13491. * @return {YAHOO.widget.Record} Record instance.
  13492. */
  13493. getRecord : function() {
  13494. return this._oRecord;
  13495. },
  13496. /**
  13497. * Returns reference to associated TD element.
  13498. *
  13499. * @method getTdEl
  13500. * @return {HTMLElement} TD element.
  13501. */
  13502. getTdEl : function() {
  13503. return this._elTd;
  13504. },
  13505. /**
  13506. * Returns container element.
  13507. *
  13508. * @method getContainerEl
  13509. * @return {HTMLElement} Reference to container element.
  13510. */
  13511. getContainerEl : function() {
  13512. return this._elContainer;
  13513. },
  13514. /**
  13515. * Nulls out the entire CellEditor instance and related objects, removes attached
  13516. * event listeners, and clears out DOM elements inside the container, removes
  13517. * container from the DOM.
  13518. *
  13519. * @method destroy
  13520. */
  13521. destroy : function() {
  13522. this.unsubscribeAll();
  13523. // Column is late-binding in attach()
  13524. var oColumn = this.getColumn();
  13525. if(oColumn) {
  13526. oColumn.editor = null;
  13527. }
  13528. var elContainer = this.getContainerEl();
  13529. Ev.purgeElement(elContainer, true);
  13530. elContainer.parentNode.removeChild(elContainer);
  13531. },
  13532. /**
  13533. * Renders DOM elements and attaches event listeners.
  13534. *
  13535. * @method render
  13536. */
  13537. render : function() {
  13538. if(this._elContainer) {
  13539. YAHOO.util.Event.purgeElement(this._elContainer, true);
  13540. this._elContainer.innerHTML = "";
  13541. }
  13542. // Render Cell Editor container element as first child of body
  13543. var elContainer = document.createElement("div");
  13544. elContainer.id = this.getId() + "-container"; // Needed for tracking blur event
  13545. elContainer.style.display = "none";
  13546. elContainer.tabIndex = 0;
  13547. elContainer.className = DT.CLASS_EDITOR;
  13548. document.body.insertBefore(elContainer, document.body.firstChild);
  13549. this._elContainer = elContainer;
  13550. // Handle ESC key
  13551. Ev.addListener(elContainer, "keydown", function(e, oSelf) {
  13552. // ESC cancels Cell Editor
  13553. if((e.keyCode == 27)) {
  13554. var target = Ev.getTarget(e);
  13555. // workaround for Mac FF3 bug that disabled clicks when ESC hit when
  13556. // select is open. [bug 2273056]
  13557. if (target.nodeName && target.nodeName.toLowerCase() === 'select') {
  13558. target.blur();
  13559. }
  13560. oSelf.cancel();
  13561. }
  13562. // Pass through event
  13563. oSelf.fireEvent("keydownEvent", {editor:this, event:e});
  13564. }, this);
  13565. this.renderForm();
  13566. // Show Save/Cancel buttons
  13567. if(!this.disableBtns) {
  13568. this.renderBtns();
  13569. }
  13570. this.doAfterRender();
  13571. },
  13572. /**
  13573. * Renders Save/Cancel buttons.
  13574. *
  13575. * @method renderBtns
  13576. */
  13577. renderBtns : function() {
  13578. // Buttons
  13579. var elBtnsDiv = this.getContainerEl().appendChild(document.createElement("div"));
  13580. elBtnsDiv.className = DT.CLASS_BUTTON;
  13581. // Save button
  13582. var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
  13583. elSaveBtn.className = DT.CLASS_DEFAULT;
  13584. elSaveBtn.innerHTML = this.LABEL_SAVE;
  13585. Ev.addListener(elSaveBtn, "click", function(oArgs) {
  13586. this.save();
  13587. }, this, true);
  13588. this._elSaveBtn = elSaveBtn;
  13589. // Cancel button
  13590. var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
  13591. elCancelBtn.innerHTML = this.LABEL_CANCEL;
  13592. Ev.addListener(elCancelBtn, "click", function(oArgs) {
  13593. this.cancel();
  13594. }, this, true);
  13595. this._elCancelBtn = elCancelBtn;
  13596. },
  13597. /**
  13598. * Attach CellEditor for a new interaction.
  13599. *
  13600. * @method attach
  13601. * @param oDataTable {YAHOO.widget.DataTable} Associated DataTable instance.
  13602. * @param elCell {HTMLElement} Cell to edit.
  13603. */
  13604. attach : function(oDataTable, elCell) {
  13605. // Validate
  13606. if(oDataTable instanceof YAHOO.widget.DataTable) {
  13607. this._oDataTable = oDataTable;
  13608. // Validate cell
  13609. elCell = oDataTable.getTdEl(elCell);
  13610. if(elCell) {
  13611. this._elTd = elCell;
  13612. // Validate Column
  13613. var oColumn = oDataTable.getColumn(elCell);
  13614. if(oColumn) {
  13615. this._oColumn = oColumn;
  13616. // Validate Record
  13617. var oRecord = oDataTable.getRecord(elCell);
  13618. if(oRecord) {
  13619. this._oRecord = oRecord;
  13620. var value = oRecord.getData(this.getColumn().getField());
  13621. this.value = (value !== undefined) ? value : this.defaultValue;
  13622. return true;
  13623. }
  13624. }
  13625. }
  13626. }
  13627. YAHOO.log("Could not attach CellEditor","error",this.toString());
  13628. return false;
  13629. },
  13630. /**
  13631. * Moves container into position for display.
  13632. *
  13633. * @method move
  13634. */
  13635. move : function() {
  13636. // Move Editor
  13637. var elContainer = this.getContainerEl(),
  13638. elTd = this.getTdEl(),
  13639. x = Dom.getX(elTd),
  13640. y = Dom.getY(elTd);
  13641. //TODO: remove scrolling logic
  13642. // SF doesn't get xy for cells in scrolling table
  13643. // when tbody display is set to block
  13644. if(isNaN(x) || isNaN(y)) {
  13645. var elTbody = this.getDataTable().getTbodyEl();
  13646. x = elTd.offsetLeft + // cell pos relative to table
  13647. Dom.getX(elTbody.parentNode) - // plus table pos relative to document
  13648. elTbody.scrollLeft; // minus tbody scroll
  13649. y = elTd.offsetTop + // cell pos relative to table
  13650. Dom.getY(elTbody.parentNode) - // plus table pos relative to document
  13651. elTbody.scrollTop + // minus tbody scroll
  13652. this.getDataTable().getTheadEl().offsetHeight; // account for fixed THEAD cells
  13653. }
  13654. elContainer.style.left = x + "px";
  13655. elContainer.style.top = y + "px";
  13656. },
  13657. /**
  13658. * Displays CellEditor UI in the correct position.
  13659. *
  13660. * @method show
  13661. */
  13662. show : function() {
  13663. this.resetForm();
  13664. this.isActive = true;
  13665. this.getContainerEl().style.display = "";
  13666. this.focus();
  13667. this.fireEvent("showEvent", {editor:this});
  13668. YAHOO.log("CellEditor shown", "info", this.toString());
  13669. },
  13670. /**
  13671. * Fires blockEvent
  13672. *
  13673. * @method block
  13674. */
  13675. block : function() {
  13676. this.fireEvent("blockEvent", {editor:this});
  13677. YAHOO.log("CellEditor blocked", "info", this.toString());
  13678. },
  13679. /**
  13680. * Fires unblockEvent
  13681. *
  13682. * @method unblock
  13683. */
  13684. unblock : function() {
  13685. this.fireEvent("unblockEvent", {editor:this});
  13686. YAHOO.log("CellEditor unblocked", "info", this.toString());
  13687. },
  13688. /**
  13689. * Saves value of CellEditor and hides UI.
  13690. *
  13691. * @method save
  13692. */
  13693. save : function() {
  13694. // Get new value
  13695. var inputValue = this.getInputValue();
  13696. var validValue = inputValue;
  13697. // Validate new value
  13698. if(this.validator) {
  13699. validValue = this.validator.call(this.getDataTable(), inputValue, this.value, this);
  13700. if(validValue === undefined ) {
  13701. if(this.resetInvalidData) {
  13702. this.resetForm();
  13703. }
  13704. this.fireEvent("invalidDataEvent",
  13705. {editor:this, oldData:this.value, newData:inputValue});
  13706. YAHOO.log("Could not save Cell Editor input due to invalid data " +
  13707. lang.dump(inputValue), "warn", this.toString());
  13708. return;
  13709. }
  13710. }
  13711. var oSelf = this;
  13712. var finishSave = function(bSuccess, oNewValue) {
  13713. var oOrigValue = oSelf.value;
  13714. if(bSuccess) {
  13715. // Update new value
  13716. oSelf.value = oNewValue;
  13717. oSelf.getDataTable().updateCell(oSelf.getRecord(), oSelf.getColumn(), oNewValue);
  13718. // Hide CellEditor
  13719. oSelf.getContainerEl().style.display = "none";
  13720. oSelf.isActive = false;
  13721. oSelf.getDataTable()._oCellEditor = null;
  13722. oSelf.fireEvent("saveEvent",
  13723. {editor:oSelf, oldData:oOrigValue, newData:oSelf.value});
  13724. YAHOO.log("Cell Editor input saved", "info", this.toString());
  13725. }
  13726. else {
  13727. oSelf.resetForm();
  13728. oSelf.fireEvent("revertEvent",
  13729. {editor:oSelf, oldData:oOrigValue, newData:oNewValue});
  13730. YAHOO.log("Could not save Cell Editor input " +
  13731. lang.dump(oNewValue), "warn", oSelf.toString());
  13732. }
  13733. oSelf.unblock();
  13734. };
  13735. this.block();
  13736. if(lang.isFunction(this.asyncSubmitter)) {
  13737. this.asyncSubmitter.call(this, finishSave, validValue);
  13738. }
  13739. else {
  13740. finishSave(true, validValue);
  13741. }
  13742. },
  13743. /**
  13744. * Cancels CellEditor input and hides UI.
  13745. *
  13746. * @method cancel
  13747. */
  13748. cancel : function() {
  13749. if(this.isActive) {
  13750. this.getContainerEl().style.display = "none";
  13751. this.isActive = false;
  13752. this.getDataTable()._oCellEditor = null;
  13753. this.fireEvent("cancelEvent", {editor:this});
  13754. YAHOO.log("CellEditor canceled", "info", this.toString());
  13755. }
  13756. else {
  13757. YAHOO.log("Unable to cancel CellEditor", "warn", this.toString());
  13758. }
  13759. },
  13760. /**
  13761. * Renders form elements.
  13762. *
  13763. * @method renderForm
  13764. */
  13765. renderForm : function() {
  13766. // To be implemented by subclass
  13767. },
  13768. /**
  13769. * Access to add additional event listeners.
  13770. *
  13771. * @method doAfterRender
  13772. */
  13773. doAfterRender : function() {
  13774. // To be implemented by subclass
  13775. },
  13776. /**
  13777. * After rendering form, if disabledBtns is set to true, then sets up a mechanism
  13778. * to save input without them.
  13779. *
  13780. * @method handleDisabledBtns
  13781. */
  13782. handleDisabledBtns : function() {
  13783. // To be implemented by subclass
  13784. },
  13785. /**
  13786. * Resets CellEditor UI to initial state.
  13787. *
  13788. * @method resetForm
  13789. */
  13790. resetForm : function() {
  13791. // To be implemented by subclass
  13792. },
  13793. /**
  13794. * Sets focus in CellEditor.
  13795. *
  13796. * @method focus
  13797. */
  13798. focus : function() {
  13799. // To be implemented by subclass
  13800. },
  13801. /**
  13802. * Retrieves input value from CellEditor.
  13803. *
  13804. * @method getInputValue
  13805. */
  13806. getInputValue : function() {
  13807. // To be implemented by subclass
  13808. }
  13809. };
  13810. lang.augmentProto(BCE, util.EventProvider);
  13811. /////////////////////////////////////////////////////////////////////////////
  13812. //
  13813. // Custom Events
  13814. //
  13815. /////////////////////////////////////////////////////////////////////////////
  13816. /**
  13817. * Fired when a CellEditor is shown.
  13818. *
  13819. * @event showEvent
  13820. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  13821. */
  13822. /**
  13823. * Fired when a CellEditor has a keydown.
  13824. *
  13825. * @event keydownEvent
  13826. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  13827. * @param oArgs.event {HTMLEvent} The event object.
  13828. */
  13829. /**
  13830. * Fired when a CellEditor input is reverted due to invalid data.
  13831. *
  13832. * @event invalidDataEvent
  13833. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  13834. * @param oArgs.newData {Object} New data value from form input field.
  13835. * @param oArgs.oldData {Object} Old data value.
  13836. */
  13837. /**
  13838. * Fired when a CellEditor input is reverted due to asyncSubmitter failure.
  13839. *
  13840. * @event revertEvent
  13841. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  13842. * @param oArgs.newData {Object} New data value from form input field.
  13843. * @param oArgs.oldData {Object} Old data value.
  13844. */
  13845. /**
  13846. * Fired when a CellEditor input is saved.
  13847. *
  13848. * @event saveEvent
  13849. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  13850. * @param oArgs.newData {Object} New data value from form input field.
  13851. * @param oArgs.oldData {Object} Old data value.
  13852. */
  13853. /**
  13854. * Fired when a CellEditor input is canceled.
  13855. *
  13856. * @event cancelEvent
  13857. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  13858. */
  13859. /**
  13860. * Fired when a CellEditor has a blur event.
  13861. *
  13862. * @event blurEvent
  13863. * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
  13864. */
  13865. /****************************************************************************/
  13866. /****************************************************************************/
  13867. /****************************************************************************/
  13868. /**
  13869. * The CheckboxCellEditor class provides functionality for inline editing
  13870. * DataTable cell data with checkboxes.
  13871. *
  13872. * @namespace YAHOO.widget
  13873. * @class CheckboxCellEditor
  13874. * @extends YAHOO.widget.BaseCellEditor
  13875. * @constructor
  13876. * @param oConfigs {Object} (Optional) Object literal of configs.
  13877. */
  13878. widget.CheckboxCellEditor = function(oConfigs) {
  13879. this._sId = "yui-checkboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
  13880. widget.CheckboxCellEditor.superclass.constructor.call(this, "checkbox", oConfigs);
  13881. };
  13882. // CheckboxCellEditor extends BaseCellEditor
  13883. lang.extend(widget.CheckboxCellEditor, BCE, {
  13884. /////////////////////////////////////////////////////////////////////////////
  13885. //
  13886. // CheckboxCellEditor public properties
  13887. //
  13888. /////////////////////////////////////////////////////////////////////////////
  13889. /**
  13890. * Array of checkbox values. Can either be a simple array (e.g., ["red","green","blue"])
  13891. * or a an array of objects (e.g., [{label:"red", value:"#FF0000"},
  13892. * {label:"green", value:"#00FF00"}, {label:"blue", value:"#0000FF"}]).
  13893. *
  13894. * @property checkboxOptions
  13895. * @type String[] | Object[]
  13896. */
  13897. checkboxOptions : null,
  13898. /**
  13899. * Reference to the checkbox elements.
  13900. *
  13901. * @property checkboxes
  13902. * @type HTMLElement[]
  13903. */
  13904. checkboxes : null,
  13905. /**
  13906. * Array of checked values
  13907. *
  13908. * @property value
  13909. * @type String[]
  13910. */
  13911. value : null,
  13912. /////////////////////////////////////////////////////////////////////////////
  13913. //
  13914. // CheckboxCellEditor public methods
  13915. //
  13916. /////////////////////////////////////////////////////////////////////////////
  13917. /**
  13918. * Render a form with input(s) type=checkbox.
  13919. *
  13920. * @method renderForm
  13921. */
  13922. renderForm : function() {
  13923. if(lang.isArray(this.checkboxOptions)) {
  13924. var checkboxOption, checkboxValue, checkboxId, elLabel, j, len;
  13925. // Create the checkbox buttons in an IE-friendly way...
  13926. for(j=0,len=this.checkboxOptions.length; j<len; j++) {
  13927. checkboxOption = this.checkboxOptions[j];
  13928. checkboxValue = lang.isValue(checkboxOption.value) ?
  13929. checkboxOption.value : checkboxOption;
  13930. checkboxId = this.getId() + "-chk" + j;
  13931. this.getContainerEl().innerHTML += "<input type=\"checkbox\"" +
  13932. " id=\"" + checkboxId + "\"" + // Needed for label
  13933. " value=\"" + checkboxValue + "\" />";
  13934. // Create the labels in an IE-friendly way
  13935. elLabel = this.getContainerEl().appendChild(document.createElement("label"));
  13936. elLabel.htmlFor = checkboxId;
  13937. elLabel.innerHTML = lang.isValue(checkboxOption.label) ?
  13938. checkboxOption.label : checkboxOption;
  13939. }
  13940. // Store the reference to the checkbox elements
  13941. var allCheckboxes = [];
  13942. for(j=0; j<len; j++) {
  13943. allCheckboxes[allCheckboxes.length] = this.getContainerEl().childNodes[j*2];
  13944. }
  13945. this.checkboxes = allCheckboxes;
  13946. if(this.disableBtns) {
  13947. this.handleDisabledBtns();
  13948. }
  13949. }
  13950. else {
  13951. YAHOO.log("Could not find checkboxOptions", "error", this.toString());
  13952. }
  13953. },
  13954. /**
  13955. * After rendering form, if disabledBtns is set to true, then sets up a mechanism
  13956. * to save input without them.
  13957. *
  13958. * @method handleDisabledBtns
  13959. */
  13960. handleDisabledBtns : function() {
  13961. Ev.addListener(this.getContainerEl(), "click", function(v){
  13962. if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
  13963. // Save on blur
  13964. this.save();
  13965. }
  13966. }, this, true);
  13967. },
  13968. /**
  13969. * Resets CheckboxCellEditor UI to initial state.
  13970. *
  13971. * @method resetForm
  13972. */
  13973. resetForm : function() {
  13974. // Normalize to array
  13975. var originalValues = lang.isArray(this.value) ? this.value : [this.value];
  13976. // Match checks to value
  13977. for(var i=0, j=this.checkboxes.length; i<j; i++) {
  13978. this.checkboxes[i].checked = false;
  13979. for(var k=0, len=originalValues.length; k<len; k++) {
  13980. if(this.checkboxes[i].value === originalValues[k]) {
  13981. this.checkboxes[i].checked = true;
  13982. }
  13983. }
  13984. }
  13985. },
  13986. /**
  13987. * Sets focus in CheckboxCellEditor.
  13988. *
  13989. * @method focus
  13990. */
  13991. focus : function() {
  13992. this.checkboxes[0].focus();
  13993. },
  13994. /**
  13995. * Retrieves input value from CheckboxCellEditor.
  13996. *
  13997. * @method getInputValue
  13998. */
  13999. getInputValue : function() {
  14000. var checkedValues = [];
  14001. for(var i=0, j=this.checkboxes.length; i<j; i++) {
  14002. if(this.checkboxes[i].checked) {
  14003. checkedValues[checkedValues.length] = this.checkboxes[i].value;
  14004. }
  14005. }
  14006. return checkedValues;
  14007. }
  14008. });
  14009. // Copy static members to CheckboxCellEditor class
  14010. lang.augmentObject(widget.CheckboxCellEditor, BCE);
  14011. /****************************************************************************/
  14012. /****************************************************************************/
  14013. /****************************************************************************/
  14014. /**
  14015. * The DataCellEditor class provides functionality for inline editing
  14016. * DataTable cell data with a YUI Calendar.
  14017. *
  14018. * @namespace YAHOO.widget
  14019. * @class DateCellEditor
  14020. * @extends YAHOO.widget.BaseCellEditor
  14021. * @constructor
  14022. * @param oConfigs {Object} (Optional) Object literal of configs.
  14023. */
  14024. widget.DateCellEditor = function(oConfigs) {
  14025. this._sId = "yui-dateceditor" + YAHOO.widget.BaseCellEditor._nCount++;
  14026. widget.DateCellEditor.superclass.constructor.call(this, "date", oConfigs);
  14027. };
  14028. // CheckboxCellEditor extends BaseCellEditor
  14029. lang.extend(widget.DateCellEditor, BCE, {
  14030. /////////////////////////////////////////////////////////////////////////////
  14031. //
  14032. // DateCellEditor public properties
  14033. //
  14034. /////////////////////////////////////////////////////////////////////////////
  14035. /**
  14036. * Reference to Calendar instance.
  14037. *
  14038. * @property calendar
  14039. * @type YAHOO.widget.Calendar
  14040. */
  14041. calendar : null,
  14042. /**
  14043. * Configs for the calendar instance, to be passed to Calendar constructor.
  14044. *
  14045. * @property calendarOptions
  14046. * @type Object
  14047. */
  14048. calendarOptions : null,
  14049. /**
  14050. * Default value.
  14051. *
  14052. * @property defaultValue
  14053. * @type Date
  14054. * @default new Date()
  14055. */
  14056. defaultValue : new Date(),
  14057. /////////////////////////////////////////////////////////////////////////////
  14058. //
  14059. // DateCellEditor public methods
  14060. //
  14061. /////////////////////////////////////////////////////////////////////////////
  14062. /**
  14063. * Render a Calendar.
  14064. *
  14065. * @method renderForm
  14066. */
  14067. renderForm : function() {
  14068. // Calendar widget
  14069. if(YAHOO.widget.Calendar) {
  14070. var calContainer = this.getContainerEl().appendChild(document.createElement("div"));
  14071. calContainer.id = this.getId() + "-dateContainer"; // Needed for Calendar constructor
  14072. var calendar =
  14073. new YAHOO.widget.Calendar(this.getId() + "-date",
  14074. calContainer.id, this.calendarOptions);
  14075. calendar.render();
  14076. calContainer.style.cssFloat = "none";
  14077. if(ua.ie) {
  14078. var calFloatClearer = this.getContainerEl().appendChild(document.createElement("div"));
  14079. calFloatClearer.style.clear = "both";
  14080. }
  14081. this.calendar = calendar;
  14082. if(this.disableBtns) {
  14083. this.handleDisabledBtns();
  14084. }
  14085. }
  14086. else {
  14087. YAHOO.log("Could not find YUI Calendar", "error", this.toString());
  14088. }
  14089. },
  14090. /**
  14091. * After rendering form, if disabledBtns is set to true, then sets up a mechanism
  14092. * to save input without them.
  14093. *
  14094. * @method handleDisabledBtns
  14095. */
  14096. handleDisabledBtns : function() {
  14097. this.calendar.selectEvent.subscribe(function(v){
  14098. // Save on select
  14099. this.save();
  14100. }, this, true);
  14101. },
  14102. /**
  14103. * Resets DateCellEditor UI to initial state.
  14104. *
  14105. * @method resetForm
  14106. */
  14107. resetForm : function() {
  14108. var value = this.value;
  14109. var selectedValue = (value.getMonth()+1)+"/"+value.getDate()+"/"+value.getFullYear();
  14110. this.calendar.cfg.setProperty("selected",selectedValue,false);
  14111. this.calendar.render();
  14112. },
  14113. /**
  14114. * Sets focus in DateCellEditor.
  14115. *
  14116. * @method focus
  14117. */
  14118. focus : function() {
  14119. // To be impmlemented by subclass
  14120. },
  14121. /**
  14122. * Retrieves input value from DateCellEditor.
  14123. *
  14124. * @method getInputValue
  14125. */
  14126. getInputValue : function() {
  14127. return this.calendar.getSelectedDates()[0];
  14128. }
  14129. });
  14130. // Copy static members to DateCellEditor class
  14131. lang.augmentObject(widget.DateCellEditor, BCE);
  14132. /****************************************************************************/
  14133. /****************************************************************************/
  14134. /****************************************************************************/
  14135. /**
  14136. * The DropdownCellEditor class provides functionality for inline editing
  14137. * DataTable cell data a SELECT element.
  14138. *
  14139. * @namespace YAHOO.widget
  14140. * @class DropdownCellEditor
  14141. * @extends YAHOO.widget.BaseCellEditor
  14142. * @constructor
  14143. * @param oConfigs {Object} (Optional) Object literal of configs.
  14144. */
  14145. widget.DropdownCellEditor = function(oConfigs) {
  14146. this._sId = "yui-dropdownceditor" + YAHOO.widget.BaseCellEditor._nCount++;
  14147. widget.DropdownCellEditor.superclass.constructor.call(this, "dropdown", oConfigs);
  14148. };
  14149. // DropdownCellEditor extends BaseCellEditor
  14150. lang.extend(widget.DropdownCellEditor, BCE, {
  14151. /////////////////////////////////////////////////////////////////////////////
  14152. //
  14153. // DropdownCellEditor public properties
  14154. //
  14155. /////////////////////////////////////////////////////////////////////////////
  14156. /**
  14157. * Array of dropdown values. Can either be a simple array (e.g.,
  14158. * ["Alabama","Alaska","Arizona","Arkansas"]) or a an array of objects (e.g.,
  14159. * [{label:"Alabama", value:"AL"}, {label:"Alaska", value:"AK"},
  14160. * {label:"Arizona", value:"AZ"}, {label:"Arkansas", value:"AR"}]).
  14161. *
  14162. * @property dropdownOptions
  14163. * @type String[] | Object[]
  14164. */
  14165. dropdownOptions : null,
  14166. /**
  14167. * Reference to Dropdown element.
  14168. *
  14169. * @property dropdown
  14170. * @type HTMLElement
  14171. */
  14172. dropdown : null,
  14173. /**
  14174. * Enables multi-select.
  14175. *
  14176. * @property multiple
  14177. * @type Boolean
  14178. */
  14179. multiple : false,
  14180. /**
  14181. * Specifies number of visible options.
  14182. *
  14183. * @property size
  14184. * @type Number
  14185. */
  14186. size : null,
  14187. /////////////////////////////////////////////////////////////////////////////
  14188. //
  14189. // DropdownCellEditor public methods
  14190. //
  14191. /////////////////////////////////////////////////////////////////////////////
  14192. /**
  14193. * Render a form with select element.
  14194. *
  14195. * @method renderForm
  14196. */
  14197. renderForm : function() {
  14198. var elDropdown = this.getContainerEl().appendChild(document.createElement("select"));
  14199. elDropdown.style.zoom = 1;
  14200. if(this.multiple) {
  14201. elDropdown.multiple = "multiple";
  14202. }
  14203. if(lang.isNumber(this.size)) {
  14204. elDropdown.size = this.size;
  14205. }
  14206. this.dropdown = elDropdown;
  14207. if(lang.isArray(this.dropdownOptions)) {
  14208. var dropdownOption, elOption;
  14209. for(var i=0, j=this.dropdownOptions.length; i<j; i++) {
  14210. dropdownOption = this.dropdownOptions[i];
  14211. elOption = document.createElement("option");
  14212. elOption.value = (lang.isValue(dropdownOption.value)) ?
  14213. dropdownOption.value : dropdownOption;
  14214. elOption.innerHTML = (lang.isValue(dropdownOption.label)) ?
  14215. dropdownOption.label : dropdownOption;
  14216. elOption = elDropdown.appendChild(elOption);
  14217. }
  14218. if(this.disableBtns) {
  14219. this.handleDisabledBtns();
  14220. }
  14221. }
  14222. },
  14223. /**
  14224. * After rendering form, if disabledBtns is set to true, then sets up a mechanism
  14225. * to save input without them.
  14226. *
  14227. * @method handleDisabledBtns
  14228. */
  14229. handleDisabledBtns : function() {
  14230. // Save on blur for multi-select
  14231. if(this.multiple) {
  14232. Ev.addListener(this.dropdown, "blur", function(v){
  14233. // Save on change
  14234. this.save();
  14235. }, this, true);
  14236. }
  14237. // Save on change for single-select
  14238. else {
  14239. Ev.addListener(this.dropdown, "change", function(v){
  14240. // Save on change
  14241. this.save();
  14242. }, this, true);
  14243. }
  14244. },
  14245. /**
  14246. * Resets DropdownCellEditor UI to initial state.
  14247. *
  14248. * @method resetForm
  14249. */
  14250. resetForm : function() {
  14251. var allOptions = this.dropdown.options,
  14252. i=0, j=allOptions.length;
  14253. // Look for multi-select selections
  14254. if(lang.isArray(this.value)) {
  14255. var allValues = this.value,
  14256. m=0, n=allValues.length,
  14257. hash = {};
  14258. // Reset all selections and stash options in a value hash
  14259. for(; i<j; i++) {
  14260. allOptions[i].selected = false;
  14261. hash[allOptions[i].value] = allOptions[i];
  14262. }
  14263. for(; m<n; m++) {
  14264. if(hash[allValues[m]]) {
  14265. hash[allValues[m]].selected = true;
  14266. }
  14267. }
  14268. }
  14269. // Only need to look for a single selection
  14270. else {
  14271. for(; i<j; i++) {
  14272. if(this.value === allOptions[i].value) {
  14273. allOptions[i].selected = true;
  14274. }
  14275. }
  14276. }
  14277. },
  14278. /**
  14279. * Sets focus in DropdownCellEditor.
  14280. *
  14281. * @method focus
  14282. */
  14283. focus : function() {
  14284. this.getDataTable()._focusEl(this.dropdown);
  14285. },
  14286. /**
  14287. * Retrieves input value from DropdownCellEditor.
  14288. *
  14289. * @method getInputValue
  14290. */
  14291. getInputValue : function() {
  14292. var allOptions = this.dropdown.options;
  14293. // Look for multiple selections
  14294. if(this.multiple) {
  14295. var values = [],
  14296. i=0, j=allOptions.length;
  14297. for(; i<j; i++) {
  14298. if(allOptions[i].selected) {
  14299. values.push(allOptions[i].value);
  14300. }
  14301. }
  14302. return values;
  14303. }
  14304. // Only need to look for single selection
  14305. else {
  14306. return allOptions[allOptions.selectedIndex].value;
  14307. }
  14308. }
  14309. });
  14310. // Copy static members to DropdownCellEditor class
  14311. lang.augmentObject(widget.DropdownCellEditor, BCE);
  14312. /****************************************************************************/
  14313. /****************************************************************************/
  14314. /****************************************************************************/
  14315. /**
  14316. * The RadioCellEditor class provides functionality for inline editing
  14317. * DataTable cell data with radio buttons.
  14318. *
  14319. * @namespace YAHOO.widget
  14320. * @class RadioCellEditor
  14321. * @extends YAHOO.widget.BaseCellEditor
  14322. * @constructor
  14323. * @param oConfigs {Object} (Optional) Object literal of configs.
  14324. */
  14325. widget.RadioCellEditor = function(oConfigs) {
  14326. this._sId = "yui-radioceditor" + YAHOO.widget.BaseCellEditor._nCount++;
  14327. widget.RadioCellEditor.superclass.constructor.call(this, "radio", oConfigs);
  14328. };
  14329. // RadioCellEditor extends BaseCellEditor
  14330. lang.extend(widget.RadioCellEditor, BCE, {
  14331. /////////////////////////////////////////////////////////////////////////////
  14332. //
  14333. // RadioCellEditor public properties
  14334. //
  14335. /////////////////////////////////////////////////////////////////////////////
  14336. /**
  14337. * Reference to radio elements.
  14338. *
  14339. * @property radios
  14340. * @type HTMLElement[]
  14341. */
  14342. radios : null,
  14343. /**
  14344. * Array of radio values. Can either be a simple array (e.g., ["yes","no","maybe"])
  14345. * or a an array of objects (e.g., [{label:"yes", value:1}, {label:"no", value:-1},
  14346. * {label:"maybe", value:0}]).
  14347. *
  14348. * @property radioOptions
  14349. * @type String[] | Object[]
  14350. */
  14351. radioOptions : null,
  14352. /////////////////////////////////////////////////////////////////////////////
  14353. //
  14354. // RadioCellEditor public methods
  14355. //
  14356. /////////////////////////////////////////////////////////////////////////////
  14357. /**
  14358. * Render a form with input(s) type=radio.
  14359. *
  14360. * @method renderForm
  14361. */
  14362. renderForm : function() {
  14363. if(lang.isArray(this.radioOptions)) {
  14364. var radioOption, radioValue, radioId, elLabel;
  14365. // Create the radio buttons in an IE-friendly way
  14366. for(var i=0, len=this.radioOptions.length; i<len; i++) {
  14367. radioOption = this.radioOptions[i];
  14368. radioValue = lang.isValue(radioOption.value) ?
  14369. radioOption.value : radioOption;
  14370. radioId = this.getId() + "-radio" + i;
  14371. this.getContainerEl().innerHTML += "<input type=\"radio\"" +
  14372. " name=\"" + this.getId() + "\"" +
  14373. " value=\"" + radioValue + "\"" +
  14374. " id=\"" + radioId + "\" />"; // Needed for label
  14375. // Create the labels in an IE-friendly way
  14376. elLabel = this.getContainerEl().appendChild(document.createElement("label"));
  14377. elLabel.htmlFor = radioId;
  14378. elLabel.innerHTML = (lang.isValue(radioOption.label)) ?
  14379. radioOption.label : radioOption;
  14380. }
  14381. // Store the reference to the checkbox elements
  14382. var allRadios = [],
  14383. elRadio;
  14384. for(var j=0; j<len; j++) {
  14385. elRadio = this.getContainerEl().childNodes[j*2];
  14386. allRadios[allRadios.length] = elRadio;
  14387. }
  14388. this.radios = allRadios;
  14389. if(this.disableBtns) {
  14390. this.handleDisabledBtns();
  14391. }
  14392. }
  14393. else {
  14394. YAHOO.log("Could not find radioOptions", "error", this.toString());
  14395. }
  14396. },
  14397. /**
  14398. * After rendering form, if disabledBtns is set to true, then sets up a mechanism
  14399. * to save input without them.
  14400. *
  14401. * @method handleDisabledBtns
  14402. */
  14403. handleDisabledBtns : function() {
  14404. Ev.addListener(this.getContainerEl(), "click", function(v){
  14405. if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
  14406. // Save on blur
  14407. this.save();
  14408. }
  14409. }, this, true);
  14410. },
  14411. /**
  14412. * Resets RadioCellEditor UI to initial state.
  14413. *
  14414. * @method resetForm
  14415. */
  14416. resetForm : function() {
  14417. for(var i=0, j=this.radios.length; i<j; i++) {
  14418. var elRadio = this.radios[i];
  14419. if(this.value === elRadio.value) {
  14420. elRadio.checked = true;
  14421. return;
  14422. }
  14423. }
  14424. },
  14425. /**
  14426. * Sets focus in RadioCellEditor.
  14427. *
  14428. * @method focus
  14429. */
  14430. focus : function() {
  14431. for(var i=0, j=this.radios.length; i<j; i++) {
  14432. if(this.radios[i].checked) {
  14433. this.radios[i].focus();
  14434. return;
  14435. }
  14436. }
  14437. },
  14438. /**
  14439. * Retrieves input value from RadioCellEditor.
  14440. *
  14441. * @method getInputValue
  14442. */
  14443. getInputValue : function() {
  14444. for(var i=0, j=this.radios.length; i<j; i++) {
  14445. if(this.radios[i].checked) {
  14446. return this.radios[i].value;
  14447. }
  14448. }
  14449. }
  14450. });
  14451. // Copy static members to RadioCellEditor class
  14452. lang.augmentObject(widget.RadioCellEditor, BCE);
  14453. /****************************************************************************/
  14454. /****************************************************************************/
  14455. /****************************************************************************/
  14456. /**
  14457. * The TextareaCellEditor class provides functionality for inline editing
  14458. * DataTable cell data with a TEXTAREA element.
  14459. *
  14460. * @namespace YAHOO.widget
  14461. * @class TextareaCellEditor
  14462. * @extends YAHOO.widget.BaseCellEditor
  14463. * @constructor
  14464. * @param oConfigs {Object} (Optional) Object literal of configs.
  14465. */
  14466. widget.TextareaCellEditor = function(oConfigs) {
  14467. this._sId = "yui-textareaceditor" + YAHOO.widget.BaseCellEditor._nCount++;
  14468. widget.TextareaCellEditor.superclass.constructor.call(this, "textarea", oConfigs);
  14469. };
  14470. // TextareaCellEditor extends BaseCellEditor
  14471. lang.extend(widget.TextareaCellEditor, BCE, {
  14472. /////////////////////////////////////////////////////////////////////////////
  14473. //
  14474. // TextareaCellEditor public properties
  14475. //
  14476. /////////////////////////////////////////////////////////////////////////////
  14477. /**
  14478. * Reference to textarea element.
  14479. *
  14480. * @property textarea
  14481. * @type HTMLElement
  14482. */
  14483. textarea : null,
  14484. /////////////////////////////////////////////////////////////////////////////
  14485. //
  14486. // TextareaCellEditor public methods
  14487. //
  14488. /////////////////////////////////////////////////////////////////////////////
  14489. /**
  14490. * Render a form with textarea.
  14491. *
  14492. * @method renderForm
  14493. */
  14494. renderForm : function() {
  14495. var elTextarea = this.getContainerEl().appendChild(document.createElement("textarea"));
  14496. this.textarea = elTextarea;
  14497. if(this.disableBtns) {
  14498. this.handleDisabledBtns();
  14499. }
  14500. },
  14501. /**
  14502. * After rendering form, if disabledBtns is set to true, then sets up a mechanism
  14503. * to save input without them.
  14504. *
  14505. * @method handleDisabledBtns
  14506. */
  14507. handleDisabledBtns : function() {
  14508. Ev.addListener(this.textarea, "blur", function(v){
  14509. // Save on blur
  14510. this.save();
  14511. }, this, true);
  14512. },
  14513. /**
  14514. * Moves TextareaCellEditor UI to a cell.
  14515. *
  14516. * @method move
  14517. */
  14518. move : function() {
  14519. this.textarea.style.width = this.getTdEl().offsetWidth + "px";
  14520. this.textarea.style.height = "3em";
  14521. YAHOO.widget.TextareaCellEditor.superclass.move.call(this);
  14522. },
  14523. /**
  14524. * Resets TextareaCellEditor UI to initial state.
  14525. *
  14526. * @method resetForm
  14527. */
  14528. resetForm : function() {
  14529. this.textarea.value = this.value;
  14530. },
  14531. /**
  14532. * Sets focus in TextareaCellEditor.
  14533. *
  14534. * @method focus
  14535. */
  14536. focus : function() {
  14537. // Bug 2303181, Bug 2263600
  14538. this.getDataTable()._focusEl(this.textarea);
  14539. this.textarea.select();
  14540. },
  14541. /**
  14542. * Retrieves input value from TextareaCellEditor.
  14543. *
  14544. * @method getInputValue
  14545. */
  14546. getInputValue : function() {
  14547. return this.textarea.value;
  14548. }
  14549. });
  14550. // Copy static members to TextareaCellEditor class
  14551. lang.augmentObject(widget.TextareaCellEditor, BCE);
  14552. /****************************************************************************/
  14553. /****************************************************************************/
  14554. /****************************************************************************/
  14555. /**
  14556. * The TextboxCellEditor class provides functionality for inline editing
  14557. * DataTable cell data with an INPUT TYPE=TEXT element.
  14558. *
  14559. * @namespace YAHOO.widget
  14560. * @class TextboxCellEditor
  14561. * @extends YAHOO.widget.BaseCellEditor
  14562. * @constructor
  14563. * @param oConfigs {Object} (Optional) Object literal of configs.
  14564. */
  14565. widget.TextboxCellEditor = function(oConfigs) {
  14566. this._sId = "yui-textboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
  14567. widget.TextboxCellEditor.superclass.constructor.call(this, "textbox", oConfigs);
  14568. };
  14569. // TextboxCellEditor extends BaseCellEditor
  14570. lang.extend(widget.TextboxCellEditor, BCE, {
  14571. /////////////////////////////////////////////////////////////////////////////
  14572. //
  14573. // TextboxCellEditor public properties
  14574. //
  14575. /////////////////////////////////////////////////////////////////////////////
  14576. /**
  14577. * Reference to the textbox element.
  14578. *
  14579. * @property textbox
  14580. */
  14581. textbox : null,
  14582. /////////////////////////////////////////////////////////////////////////////
  14583. //
  14584. // TextboxCellEditor public methods
  14585. //
  14586. /////////////////////////////////////////////////////////////////////////////
  14587. /**
  14588. * Render a form with input type=text.
  14589. *
  14590. * @method renderForm
  14591. */
  14592. renderForm : function() {
  14593. var elTextbox;
  14594. // Bug 1802582: SF3/Mac needs a form element wrapping the input
  14595. if(ua.webkit>420) {
  14596. elTextbox = this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input"));
  14597. }
  14598. else {
  14599. elTextbox = this.getContainerEl().appendChild(document.createElement("input"));
  14600. }
  14601. elTextbox.type = "text";
  14602. this.textbox = elTextbox;
  14603. // Save on enter by default
  14604. // Bug: 1802582 Set up a listener on each textbox to track on keypress
  14605. // since SF/OP can't preventDefault on keydown
  14606. Ev.addListener(elTextbox, "keypress", function(v){
  14607. if((v.keyCode === 13)) {
  14608. // Prevent form submit
  14609. YAHOO.util.Event.preventDefault(v);
  14610. this.save();
  14611. }
  14612. }, this, true);
  14613. if(this.disableBtns) {
  14614. // By default this is no-op since enter saves by default
  14615. this.handleDisabledBtns();
  14616. }
  14617. },
  14618. /**
  14619. * Moves TextboxCellEditor UI to a cell.
  14620. *
  14621. * @method move
  14622. */
  14623. move : function() {
  14624. this.textbox.style.width = this.getTdEl().offsetWidth + "px";
  14625. widget.TextboxCellEditor.superclass.move.call(this);
  14626. },
  14627. /**
  14628. * Resets TextboxCellEditor UI to initial state.
  14629. *
  14630. * @method resetForm
  14631. */
  14632. resetForm : function() {
  14633. this.textbox.value = lang.isValue(this.value) ? this.value.toString() : "";
  14634. },
  14635. /**
  14636. * Sets focus in TextboxCellEditor.
  14637. *
  14638. * @method focus
  14639. */
  14640. focus : function() {
  14641. // Bug 2303181, Bug 2263600
  14642. this.getDataTable()._focusEl(this.textbox);
  14643. this.textbox.select();
  14644. },
  14645. /**
  14646. * Returns new value for TextboxCellEditor.
  14647. *
  14648. * @method getInputValue
  14649. */
  14650. getInputValue : function() {
  14651. return this.textbox.value;
  14652. }
  14653. });
  14654. // Copy static members to TextboxCellEditor class
  14655. lang.augmentObject(widget.TextboxCellEditor, BCE);
  14656. /////////////////////////////////////////////////////////////////////////////
  14657. //
  14658. // DataTable extension
  14659. //
  14660. /////////////////////////////////////////////////////////////////////////////
  14661. /**
  14662. * CellEditor subclasses.
  14663. * @property DataTable.Editors
  14664. * @type Object
  14665. * @static
  14666. */
  14667. DT.Editors = {
  14668. checkbox : widget.CheckboxCellEditor,
  14669. "date" : widget.DateCellEditor,
  14670. dropdown : widget.DropdownCellEditor,
  14671. radio : widget.RadioCellEditor,
  14672. textarea : widget.TextareaCellEditor,
  14673. textbox : widget.TextboxCellEditor
  14674. };
  14675. /****************************************************************************/
  14676. /****************************************************************************/
  14677. /****************************************************************************/
  14678. /**
  14679. * Factory class for instantiating a BaseCellEditor subclass.
  14680. *
  14681. * @namespace YAHOO.widget
  14682. * @class CellEditor
  14683. * @extends YAHOO.widget.BaseCellEditor
  14684. * @constructor
  14685. * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
  14686. * @param oConfigs {Object} (Optional) Object literal of configs.
  14687. */
  14688. widget.CellEditor = function(sType, oConfigs) {
  14689. // Point to one of the subclasses
  14690. if(sType && DT.Editors[sType]) {
  14691. lang.augmentObject(BCE, DT.Editors[sType]);
  14692. return new DT.Editors[sType](oConfigs);
  14693. }
  14694. else {
  14695. return new BCE(null, oConfigs);
  14696. }
  14697. };
  14698. var CE = widget.CellEditor;
  14699. // Copy static members to CellEditor class
  14700. lang.augmentObject(CE, BCE);
  14701. })();
  14702. YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.8.0r4", build: "2449"});